On 06Apr2016 16:26, derek martin <[email protected]> wrote:
Hi Cameron,

On Wed, Apr 06, 2016 at 09:50:28AM +1000, Cameron Simpson wrote:
Consider using ${1+"$@"}, which preserves quoting.

How is this better than just "$@"?  I believe it's non-portable (and
for that reason I'm less familiar with that syntax) but if I
understand correctly it expands like:

 if $1 is set use that, otherwise use "$@" (all arguments, individually quoted)

No, it says ``if $1 is set, use "$@", otherwise use nothing''. See below.

It seems as though this always evaluates to $1 (since if $1 is unset,
$@ is also necessarily empty)... which I think is not what is needed
here.  Am I mistaken?  I believe just "$@" (including the quotes) is
what you want here.

Alas, no. And also no.

Firstly, "$@" _is_ portable; it has been around as a very special case for decades, since at least V7 UNIX.

For those unfamiliar, the shell syntax $x inserts the text from paramater "x" at that point, but unquoted: it will be broken into words on whitespace. Conversely "$x" inserts the text from paramater "x" at that point, but quoted: it is a single string, not multiple words.

For many parameters this is enough. However, the command line arguments are effectively a list of strings. The shell offers $* for the command line arguments, but that behaves exactly like any other paramater: you get all the words broken up if upquoted, or a single string if quoted. Both are usually not correct.

Because keeping script command line arguments intact is very important the variable $@ was also made. It is just like $* except for the special case when one writes "$@", in which case it will be expanded to all the arguments correctly quoted.

So if you are writing a wrapper script, for example to invoke notmuch or mairix with some options and then the search terms supplied on the script command line, you might call notmuch like this:

 # invoke notmuch with the command line arguments, intact
 notmuch-search -S "$@"

which will work correctly even if the script was called with
However, it had a single annoying misfeature/bug:

For historic reasons, "$@" evaluates to a single "" if there were no arguments at all, introducing a spurious new empty argument. Possibly the thinking was that something like "$@" should never vanish; after all "$x" will produce an single empty string if $x is empty. The ${1+} simply avoids using "$@" at all unless there are arguments, producing correct behaviour in the no-arguments case.

More recent shells treat a bare unquotes $@ specially also: it produces the command line arguments correctly quoted like "$@", and also does the no-arguments case correctly.

However, because my scripting dates from tling ago, I don't rely on this because some shells may not have it. hence the incantation, which is portable.

The core lesson here is to pretty much never use $* (actually, there are some cases for it) and prefer "$@", but when you use "$@" to be truly paranoid and robust you should protect it from misuse with ${1+"$@"}.

Cheers,
Cameron Simpson <[email protected]>

Reply via email to