## 37.5. Quoting and Command-Line Parameters

Q: I need to pass a shell script some arguments with multiple words. I thought that putting quotes (Section 27.12) around command-line arguments would group them. The shell script seems to ignore the quoting, somehow. Here's a simple example:

>     $ cat script
>        ...
>     for arg in $*
>     do
>         echo "Argument is $arg"
>     done
>     $ script '1 2 3' 4
>        ...
>     Argument is 1
>     Argument is 2
>     Argument is 3
>     Argument is 4

A: This is the way `$*` is defined to work. `$*` expands to:

>     $1 $2

[not `<">$1<"> <">$2<">` -- JP] if there are two arguments. Hence the for loop reads:

>     for arg in 1 2 3 4

Note that the quotes are gone. What you wanted the shell to see was:

>     for arg in '1 2 3' 4

You can't get that, but you can get something that is good enough:

`"$@"` Section 35.20

>     for arg in "$@"

In effect, `$@` expands to:

>     $1" "$2

Putting `""` s around `$@`, the effect is:

>     for arg in "$1" "$2"

Shell quoting is unnecessarily complex. The C shell actually has the right idea (variables can be set to "word lists"; argv is such a list), but its defaults and syntax for suppressing them make for an artless programming language:

>     foreach arg ($argv:q)      # colon q ?!?

For the special case of iterating a shell variable over the argument list as it stands at the beginning of the iteration, the Bourne shell provides the construct `for arg` `do` [i.e., no `in` `list` -- JP]:

>     for arg
>     do echo "Argument is $arg"
>     done

The example produces:

>     Argument is 1 2 3
>     Argument is 4

`"$@"` is still needed for passing argument lists to other programs. Unfortunately, since `$@` is defined as expanding to:

>     $1" "$2...$n-1" "$n

(where `n` is the number of arguments), when there are no arguments, `"$@"` expands to `""`, and `""` produces a single argument. [Many Unix vendors considered this a bug and changed it so that it produces no arguments. -- JP] The best solution for this is to use, for example:

>     % cat bin/okeeffe
>     #! /bin/sh
>     exec rsh okeeffe.berkeley.edu -l torek ${1+"$@"}
>     %

The construct `${1+"$@"}` means "expand `$1`, but if `$1` is not defined, use `"$@"` instead." [You don't need this on Bourne shells with the "bug fix" I mentioned, or on bash et al. -- JP] Hence, if there are no arguments, we get `$1` (which is nothing and produces no arguments); otherwise, we get `"$@"` (which expands as above). `${``var``+`instead`}` is one of several sh "expansion shortcuts" (Section 36.7). Another more generally useful one is `${``var-default``}`, which expands to `$``var`, but if var is not set, to `default` instead. All of these can be found in the manual for sh, which is worth reading several times, experimenting as you go.

bash has a variety of similar but expanded mechanisms as well, involving a colon before the modifier:

>     foo=${bar:-baz}             if bar set and non-null, substitute value, else substitute baz...
>     fum=${fee:=foe}             if fee unset or is null, set it to foe, value then substituted...
>     fiend=${jeckyll::=hyde}     set jeckyll to hyde, then substitute value... (zsh only)
>     ${required?"error"}        if required set or non-null, substitute its value,
>                                       else return "error" and exit...
>     man=${fullmoon:+wolfman}    if fullmoon set and non-null, substitute wolfman,
>                                                             else substitute nothing...

See the bash manual page's section on parameter expansion. ksh, pdksh, and zsh also have support for the same syntax; zsh has an entire manual page devoted to just parameter expansions: zshexpn(1). Poke around; there's lots of good stuff to explore.

--CT and SJC

Copyright © 2003 O'Reilly & Associates. All rights reserved.