1

tl;dr ... found a script that uses ${1+"$@"} to forward command line arguments to another script. What exactly does this do? When do you use it instead of $@ and "$@"?

There's a simple shell script called runhaskell that is distributed with ghc on Ubuntu 15.10 (and perhaps others). I am trying to figure out how to replace it with something that's aware of cabal sandboxes. The shell script looks like this. It appears to have some variables that aren't used and an unnecessary shebang-like comment (from an earlier version of the script?)

#!/bin/bash
exedir="/usr/lib/ghc/bin"
exeprog="runghc"
executablename="$exedir/$exeprog"
datadir="/usr/share"
bindir="/usr/bin"
topdir="/usr/lib/ghc"
#!/bin/sh

exec "$executablename" -f "$bindir/ghc" ${1+"$@"}

I'm having trouble understanding the last line. /usr/lib/ghc/bin/runghc is a native executable that runhaskell evidently delegates to, and one of the arguments it passes in is the location of the compiler. Okay, no problem.

What does ${1+"$@"} do and when should you use it? My best guess is that 1 is just a test that always succeeds and that this construction is just being used just to pass in the arguments verbatim. I remember POSIX requires that $@ and "$@" behave somewhat differently from what you'd expect, but I don't remember the exact details.

Greg Nisbet
  • 3,076

1 Answers1

2

${var+foo} expands to foo if variable var is set (even if empty), and nothing otherwise. In this case the variable is the first positional parameter, $1. So, the ${1+"$@"} checks if $1 is set and expands to "$@" accordingly, and is nothing otherwise.

As for $@ and "$@", $@ is subject to field splitting and filename expansion after it has expanded to the positional paramters, whereas "$@" isn't:

$ sh -c 'cd /usr; printf "%s\n" $@' _ 'a b' '*'  
a
b
bin
include
…
$ sh -c 'cd /usr; printf "%s\n" "$@"' _ 'a b' '*'
a b
*

You almost always want to use "$@" over $@.

As for ${1+"$@"} vs just "$@", in POSIX-compliant shells, both would have the same effect. According to this Stack Overflow post:

'Hysterical Raisins', aka Historical Reasons.

Once upon 20 or so years ago, some broken minor variants of the Bourne Shell substituted an empty string "" for "$@" if there were no arguments, instead of the correct, current behaviour of substituting nothing. Whether any such systems are still in use is open to debate.

The autoconf manual section on Shell Substitutions also deals with this:

There are also portability pitfalls with particular expansions:

$@

One of the most famous shell-portability issues is related to ‘"$@"’. When there are no positional arguments, Posix says that ‘"$@"’ is supposed to be equivalent to nothing, but the original Unix version 7 Bourne shell treated it as equivalent to ‘""’ instead, and this behavior survives in later implementations like Digital Unix 5.0.


Also see:

muru
  • 72,889