Weird behavior with the $@ variable Weird behavior with the $@ variable unix unix

Weird behavior with the $@ variable


The sh language spec has this to say about $@:

Expands to the positional parameters, starting from one. When the expansion occurs within double-quotes, and where field splitting (see Field Splitting ) is performed, each positional parameter shall expand as a separate field, with the provision that the expansion of the first parameter shall still be joined with the beginning part of the original word (assuming that the expanded parameter was embedded within a word), and the expansion of the last parameter shall still be joined with the last part of the original word. If there are no positional parameters, the expansion of '@' shall generate zero fields, even when '@' is double-quoted.

This means that the string "echo $@ $*" where the positional parameters are 1, 2, 3 should expand to "echo 1" "2" "3 1 2 3", so that your test 3 should output the string 1. The fact that bash incorrectly expands the string to "echo 1 2 3 1 2 3" and outputs 1 2 3 1 2 3 is indicative of a bug in bash.

The usual technique for dealing with this (yet another) oddity of sh grammar rules is to only use $@ in double quotes when it is a distinct word. In other words, it's okay to write "$@", but don't put $@ in double quotes unless it is the only thing inside the double quotes.


Using @BrianSwift's hint, everything makes perfectly sense, except for Test3 (here, it is not the case that we get three arguments with the first having a prefix of 'echo ' and the third having a suffix of ' $*'. Instead, the result is a single argument).

But that may probably be explained with the fact that both $@ and $* are "special" variables for which special code in the shell exists. I assume the existence of $* just overrides the special behaviour noticed by @BrianSwift, because $* is evaluated later.

Try swapping the order: somefunc() { sh -x "echo $* $@"; }. You will get the split-with-prefix-and-suffix behaviour again which seems to support my above assumption.

So to conclude, everything can be explained with a reference to the manual except for Test3 whose behaviour is probably a bit "underdocumented".


As I understand and read it, $@ turns into "arg1" "arg2" "arg3".

No, "$@" turns into "arg1" "arg2" "arg3". The quotes matter.