GNU Info

Info Node: (autoconf.info)Shell Substitutions

(autoconf.info)Shell Substitutions


Next: Assignments Prev: File System Conventions Up: Portable Shell
Enter node , (file) or (file)node

Shell Substitutions
===================

   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!).

`$@'
     One of the most famous shell-portability issues is related to
     `"$@"': 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}'
     Old BSD shells, including the Ultrix `sh', don't accept the colon
     for any shell substitution, and complain and die.

`${VAR=LITERAL}'
     Be sure to quote:

          : ${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}'
     On Ultrix, running

          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`'
     While in general it makes no sense, do not substitute a single
     builtin with side effects as Ash 0.2, trying to optimize, does not
     fork a sub-shell to perform the command.

     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)'
     This construct is meant to replace ``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)


automatically generated by info2www version 1.2.2.9