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]>