3

I have a Bash function that does some string manipulation on its arguments as a whole ("$@") by putting it in a local variable, something like this:

#!/bin/sh

my_func() {
    local args="$@"
    echo "args: <$args>"
}

my_func "$@"

When I run this in Bash, args contains all of the arguments that were passed:

$ bash foo.sh foo bar baz
foo bar baz

However, if I run it in Dash, only the first argument is stored:

$ dash test.sh foo bar baz
args: <foo>

Reading the section on local in the Ubuntu Wiki's "Dash as /bin/sh" page, it seems that Dash is expanding the local args="$@" line like so:

local args=foo bar baz

and therefore only putting "foo" in args and declaring bar and baz as (local?) variables. In fact, if I add echo "bar: $bar" to my_func and run it with an = in the arguments it seems to confirm that I am adding variables:

$ foo.sh foo bar=baz
args: <foo>
bar: baz

All this to say, is there a way to get the Bash-like behaviour (of $args containing "foo bar baz") in Dash?

1
  • Use local IFS=" "; local args="$*"
    – mosvy
    Jul 25 '19 at 20:22
3

The expansion of $@ in local args="$@" is unspecified by the POSIX standard. The bash shell will create a single space-delimited string containing all the positional parameters as the value for the args variable, while dash will try to execute local args="$1" "$2" "$3" (etc.)

The zsh and ksh shells behave like bash (creating a single string out of the positional parameters, although zsh would use the first character of $IFS for the delimiter) while the yash shell behaves like dash, at least in their default configurations.

In your case, you should use

my_func () {
    local args
    args="$*"

    printf 'args: <%s>\n' "$args"
}

or

my_func () {
    local args="$*"

    printf 'args: <%s>\n' "$args"
}

I'm using $* here to make it obvious that I'm constructing a single string from a list of values. The string will contain the values of the positional parameters, delimited by the first character of $IFS (a space by default).

I'm also using printf to be sure to get the correct output of the user-supplied values (see Why is printf better than echo?).

Also, your script should use #!/bin/dash as the first line rather than #!/bin/sh as local is an extension to the standard sh syntax.

11
  • In which versions of dash? local foo=bar works in both dash-0.5.8 and the related busybox ash.
    – mosvy
    Jul 25 '19 at 20:28
  • @mosvy I noticed. However, the manual does not mention that it is possible explicitly, and I'm now wondering whether this is a bug related to the expansion of "$@".
    – Kusalananda
    Jul 25 '19 at 20:29
  • Yep, that works, thank you! Yes, I guess it should be #!/bin/dash, but I hadn't put that much thought into it since it's just an example script, similarly for echo use. Jul 25 '19 at 20:33
  • The expansion of "$@" is unspecified in foo="$@", according to the standard.
    – mosvy
    Jul 25 '19 at 20:34
  • @mosvy Yes, you are correct. Better to use "$*" when creating a single string out of the positional parameters.
    – Kusalananda
    Jul 25 '19 at 20:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.