Node:Shell Substitutions, Next:Assignments, Previous:File System Conventions, Up:Portable Shell
Contrary to a persistent urban legend, the Bourne shell does not
systematically split variables and backquoted expressions, in particular
on the right-hand side of assignments and in the argument of case
.
For instance, the following code:
case "$given_srcdir" in .) top_srcdir="`echo "$dots" | sed 's,/$,,'`" *) top_srcdir="$dots$given_srcdir" ;; esac
is more readable when written as:
case $given_srcdir in .) top_srcdir=`echo "$dots" | sed 's,/$,,'` *) top_srcdir=$dots$given_srcdir ;; esac
and in fact it is even more portable: in the first case of the
first attempt, the computation of top_srcdir
is not portable,
since not all shells properly understand "`..."..."...`"
.
Worse yet, not all shells understand "`...\"...\"...`"
the same way. There is just no portable way to use double-quoted
strings inside double-quoted backquoted expressions (pfew!).
$@
"$@"
: when there are no positional arguments, it is supposed to
be equivalent to nothing. But some shells, for instance under Digital
Unix 4.0 and 5.0, will then replace it with an empty argument. To be
portable, use ${1+"$@"}
.
${var:-value}
sh
, don't accept the
colon for any shell substitution, and complain and die.
${var=literal}
: ${var='Some words'}
otherwise some shells, such as on Digital Unix V 5.0, will die because of a "bad substitution".
Solaris' /bin/sh
has a frightening bug in its interpretation
of this. Imagine you need set a variable to a string containing
}
. This }
character confuses Solaris' /bin/sh
when the affected variable was already set. This bug can be exercised
by running:
$ unset foo $ foo=${foo='}'} $ echo $foo } $ foo=${foo='}' # no error; this hints to what the bug is $ echo $foo } $ foo=${foo='}'} $ echo $foo }} ^ ugh!
It seems that }
is interpreted as matching ${
, even
though it is enclosed in single quotes. The problem doesn't happen
using double quotes.
${var=expanded-value}
default="yu,yaa" : ${var="$default"}
will set var to M-yM-uM-,M-yM-aM-a
, i.e., the 8th bit of
each char will be set. You won't observe the phenomenon using a simple
echo $var
since apparently the shell resets the 8th bit when it
expands $var. Here are two means to make this shell confess its sins:
$ cat -v <<EOF $var EOF
and
$ set | grep '^var=' | cat -v
One classic incarnation of this bug is:
default="a b c" : ${list="$default"} for c in $list; do echo $c done
You'll get a b c
on a single line. Why? Because there are no
spaces in $list
: there are M-
, i.e., spaces with the 8th
bit set, hence no IFS splitting is performed!!!
One piece of good news is that Ultrix works fine with :
${list=$default}
; i.e., if you don't quote. The bad news is
then that QNX 4.25 then sets list to the last item of
default!
The portable way out consists in using a double assignment, to switch
the 8th bit twice on Ultrix:
list=${list="$default"}...but beware of the
}
bug from Solaris (see above). For safety,
use:
test "${var+set}" = set || var={value}
`commands`
For instance, if you wanted to check that cd
is silent, do not
use test -z "`cd /`"
because the following can happen:
$ pwd /tmp $ test -n "`cd /`" && pwd /
The result of foo=`exit 1`
is left as an exercise to the reader.
$(commands)
`commands`
; they can be
nested while this is impossible to do portably with back quotes.
Unfortunately it is not yet widely supported. Most notably, even recent
releases of Solaris don't support it:
$ showrev -c /bin/sh | grep version Command version: SunOS 5.8 Generic 109324-02 February 2001 $ echo $(echo blah) syntax error: `(' unexpected
nor does IRIX 6.5's Bourne shell:
$ uname -a IRIX firebird-image 6.5 07151432 IP22 $ echo $(echo blah) $(echo blah)