Bourne | Ash |  #!  | find | ARG_MAX | Shells | whatshell | portability | permissions | UUOC | ancient | - | ../Various | HOME
"$@" | echo/printf | set -e | test | tty defs | tty chars | $() vs ) | IFS | using siginfo | nanosleep | line charset | locale


What does ${1+"$@"} mean

...and where is it necessary?

2016-09-08 (see recent changes)


It's a workaround for older releases of the traditional Bourne shell: they parse "$@" in an unexpected way if no arguments were supplied.
All traditional Bourne shells since the SVR3 release and all modern bourne compatible shells behave intuitively.
One might argue, whether the old behaviour is a bug in the very sense, or unintuitive de-facto behaviour. But it was not properly specified, yet, and definitely error-prone.

(It's also a workaround for a problem in various shells if you want to use set -u. See more at the bottom of this page, "Oh wait, ...")

Example
in a shell, where this workaround is necessary.

With the following script,

	$ cat args.sh
	for i in "$@"; do
	    echo arg: \"$i\"
	done
you get output as expected if you provide at least one argument:
	$ sh args.sh 1 2
	arg: "1"
	arg: "2"
However, without arguments, you still get (empty) output, instead of no output at all:
	$ sh args.sh
	arg: ""

Why is this relevant?

Keep in mind, that "$@" is most frequently used for passing on arguments, or for iterating over them in a loop, like above.
Thus a mechanism for collapsing the argument list to nothing--and not to the empty argument--is needed.

How does it work?

The ${1+"$@"} syntax first tests if $1 is set, that is, if there is an argument at all.
If so, then this expression is replaced with the whole "$@" argument list.
If not, then it collapses to nothing instead of an empty argument.

Why does it work this way?
The problem only arises if a variable is quoted, and $1 is not.

The workaround is not limited to this variant. These work also: ${@+"$@"} and ${*+"$@"},
but the abovementioned variant is the widely known one.

Another, historic problem

Even with the above workaround, early shells before SVR2 (7th edition, BSDs, SysIII and SVR1) discard empty arguments in the argument list.

	$ sh ./args.sh 1 '' 2
	arg: "1"
	arg: "2" 

This looks quite similar at first, but in practice it's less relevant, because dealing without any argument is much more likely to happen.

Portability

${1+"$@"} is reliable and portable -- with one exception:

zsh until release 4.3.0, in sh emulation mode (which means the option shwordsplit is set), does word splitting on the resulting arguments.

	$ sh ./args.sh '1 2'
	arg: "1"
	arg: "2"

And: keep in mind that the traditional Bourne shell requires IFS to contain a blank for "$@" and ${1+"$@"} to work as expected.


An alternative

You might just loop yourself over the arguments:

    for i
    do
	echo "$i"
    done 

interestingly also valid if merged but without semicolon:

    for i do
	echo "$i"
    done 

Although these are not documented for the Version 7 Bourne shell (and its variants on early BSDs), they work on these systems, too.
(Picked up from the german lang usenet posting <3BE2D2D3.I715123W@bigfoot.de> from Gunnar Ritter; and I could confirm this on all the flavours).

See more details about this short form of the for-loop at the end of this page.

In contrast to ${1+"$@"}, these variants even work correctly in the pre-SVR2 variants if there are empty arguments in the list (pointed out by Stéphane Chazelas and I can confirm it).
However, for passing on the arguments to another program (instead of iterating over them) ${1+"$@"} (or "$@") is certainly the only solution.


Which Bourne shells exactly need the workaround?

These shells behave the old way and need ${1+"$@"}:

In contrast, these Bourne shell variants behave the modern way:


Oh wait, there's another meaning

It's also a workaround for a problem in some shells if you want to use the flag u ("error upon using empty variables")
and "$@" (or $@, "$*", $*) is used without arguments. Example:

    $ shell -cu 'echo     "$@";  echo not reached'
    @: parameter not set
    $ shell -cu 'echo ${1+"$@"}; echo ok'
    ok

At least these shells need that workaround:


Endnote

Details about the short form of the for-loop.

Among the four variations

      $ for i
	do
      $ for i do
      $ for i; do
      $ for i;
        do 
[1] This is weird because ; (semicolon) and newline are usually equivalent.

The documentation doesn't explicitly tell if a terminator is required/allowed; it seems to read as if it's not allowed

     for name [in word ...] do list done
          Each time a for command is executed name is set to  the
          next word in the for word list If in word ...  is omit-
          ted then in "$@" is assumed.  Execution ends when there
          are no more words in the list. 
Comparison with the while control command suggests itself, but is not helpful. The while description doesn't match behaviour, because the terminator in list is not optional
(and because it's incorrect anyway, because "do list" is not optional as written, which will be fixed in the System III documentation),
     while list [do list] done

     A list is a sequence of one or more pipelines  separated  by
     ;, &, && or || and optionally terminated by ; or &.    [...]
             Newlines may appear in  a  list,  instead  of  semi-
     colons, to delimit commands.  


<http://www.in-ulm.de/~mascheck/various/bourne_args/>

Sven Mascheck