6

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:

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
args: <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="$*"
    – user313992
    Jul 25, 2019 at 20:22

1 Answer 1

5

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.
    – user313992
    Jul 25, 2019 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, 2019 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, 2019 at 20:33
  • The expansion of "$@" is unspecified in foo="$@", according to the standard.
    – user313992
    Jul 25, 2019 at 20:34
  • @mosvy Yes, you are correct. Better to use "$*" when creating a single string out of the positional parameters.
    – Kusalananda
    Jul 25, 2019 at 20:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .