DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH
 

(autoconf.info.gz) Limitations of Builtins

Info Catalog (autoconf.info.gz) Special Shell Variables (autoconf.info.gz) Portable Shell (autoconf.info.gz) Limitations of Usual Tools
 
 Limitations of Shell Builtins
 =============================
 
    No, no, we are serious: some shells do have limitations!  :)
 
    You should always keep in mind that any builtin or command may
 support options, and therefore have a very different behavior with
 arguments starting with a dash.  For instance, the innocent `echo
 "$word"' can give unexpected results when `word' starts with a dash.
 It is often possible to avoid this problem using `echo "x$word"', taking
 the `x' into account later in the pipe.
 
 `.'
      Use `.' only with regular files (use `test -f').  Bash 2.03, for
      instance, chokes on `. /dev/null'.  Also, remember that `.' uses
      `PATH' if its argument contains no slashes, so if you want to use
      `.' on a file `foo' in the current directory, you must use `.
      ./foo'.
 
 `!'
      You can't use `!'; you'll have to rewrite your code.
 
 `break'
      The use of `break 2' etc. is safe.
 
 `cd'
      POSIX 1003.1-2001 requires that `cd' must support the `-L'
      ("logical") and `-P' ("physical") options, with `-L' being the
      default.  However, traditional shells do not support these
      options, and their `cd' command has the `-P' behavior.
 
      Portable scripts should assume neither option is supported, and
      should assume neither behavior is the default.  This can be a bit
      tricky, since the POSIX default behavior means that, for example,
      `ls ..' and `cd ..' may refer to different directories if the
      current logical directory is a symbolic link.  It is safe to use
      `cd DIR' if DIR contains no `..' components.  Also,
      Autoconf-generated scripts check for this problem when computing
      variables like `ac_top_srcdir' ( Configuration Actions), so
      it is safe to `cd' to these variables.
 
      Also please see the discussion of the `pwd' command.
 
 `case'
      You don't need to quote the argument; no splitting is performed.
 
      You don't need the final `;;', but you should use it.
 
      Because of a bug in its `fnmatch', `bash' fails to properly handle
      backslashes in character classes:
 
           bash-2.02$ case /tmp in [/\\]*) echo OK;; esac
           bash-2.02$
 
      This is extremely unfortunate, since you are likely to use this
      code to handle UNIX or MS-DOS absolute paths.  To work around this
      bug, always put the backslash first:
 
           bash-2.02$ case '\TMP' in [\\/]*) echo OK;; esac
           OK
           bash-2.02$ case /tmp in [\\/]*) echo OK;; esac
           OK
 
      Some shells, such as Ash 0.3.8, are confused by an empty
      `case'/`esac':
 
           ash-0.3.8 $ case foo in esac;
           error-->Syntax error: ";" unexpected (expecting ")")
 
      Many shells still do not support parenthesized cases, which is a
      pity for those of us using tools that rely on balanced
      parentheses.  For instance, Solaris 2.8's Bourne shell:
 
           $ case foo in (foo) echo foo;; esac
           error-->syntax error: `(' unexpected
 
 `echo'
      The simple `echo' is probably the most surprising source of
      portability troubles.  It is not possible to use `echo' portably
      unless both options and escape sequences are omitted.  New
      applications which are not aiming at portability should use
      `printf' instead of `echo'.
 
      Don't expect any option.   Preset Output Variables, `ECHO_N'
      etc. for a means to simulate `-n'.
 
      Do not use backslashes in the arguments, as there is no consensus
      on their handling.  On `echo '\n' | wc -l', the `sh' of Digital
      Unix 4.0 and MIPS RISC/OS 4.52, answer 2, but the Solaris' `sh',
      Bash, and Zsh (in `sh' emulation mode) report 1.  Please note that
      the problem is truly `echo': all the shells understand `'\n'' as
      the string composed of a backslash and an `n'.
 
      Because of these problems, do not pass a string containing
      arbitrary characters to `echo'.  For example, `echo "$foo"' is safe
      if you know that FOO's value cannot contain backslashes and cannot
      start with `-', but otherwise you should use a here-document like
      this:
 
           cat <<EOF
           $foo
           EOF
 
 `exit'
      The default value of `exit' is supposed to be `$?'; unfortunately,
      some shells, such as the DJGPP port of Bash 2.04, just perform
      `exit 0'.
 
           bash-2.04$ foo=`exit 1` || echo fail
           fail
           bash-2.04$ foo=`(exit 1)` || echo fail
           fail
           bash-2.04$ foo=`(exit 1); exit` || echo fail
           bash-2.04$
 
      Using `exit $?' restores the expected behavior.
 
      Some shell scripts, such as those generated by `autoconf', use a
      trap to clean up before exiting.  If the last shell command exited
      with nonzero status, the trap also exits with nonzero status so
      that the invoker can tell that an error occurred.
 
      Unfortunately, in some shells, such as Solaris 8 `sh', an exit
      trap ignores the `exit' command's argument.  In these shells, a
      trap cannot determine whether it was invoked by plain `exit' or by
      `exit 1'.  Instead of calling `exit' directly, use the
      `AC_MSG_ERROR' macro that has a workaround for this problem.
 
 `export'
      The builtin `export' dubs a shell variable "environment variable".
      Each update of exported variables corresponds to an update of the
      environment variables.  Conversely, each environment variable
      received by the shell when it is launched should be imported as a
      shell variable marked as exported.
 
      Alas, many shells, such as Solaris 2.5, IRIX 6.3, IRIX 5.2, AIX
      4.1.5, and DU 4.0, forget to `export' the environment variables
      they receive.  As a result, two variables are coexisting: the
      environment variable and the shell variable.  The following code
      demonstrates this failure:
 
           #! /bin/sh
           echo $FOO
           FOO=bar
           echo $FOO
           exec /bin/sh $0
 
      when run with `FOO=foo' in the environment, these shells will print
      alternately `foo' and `bar', although it should only print `foo'
      and then a sequence of `bar's.
 
      Therefore you should `export' again each environment variable that
      you update.
 
 `false'
      Don't expect `false' to exit with status 1: in the native Bourne
      shell of Solaris 8 it exits with status 255.
 
 `for'
      To loop over positional arguments, use:
 
           for arg
           do
             echo "$arg"
           done
 
      You may _not_ leave the `do' on the same line as `for', since some
      shells improperly grok:
 
           for arg; do
             echo "$arg"
           done
 
      If you want to explicitly refer to the positional arguments, given
      the `$@' bug ( Shell Substitutions), use:
 
           for arg in ${1+"$@"}; do
             echo "$arg"
           done
 
      But keep in mind that Zsh, even in Bourne shell emulation mode,
      performs word splitting on `${1+"$@"}'; see  Shell
      Substitutions, item `$@', for more.
 
 `if'
      Using `!' is not portable.  Instead of:
 
           if ! cmp -s file file.new; then
             mv file.new file
           fi
 
      use:
 
           if cmp -s file file.new; then :; else
             mv file.new file
           fi
 
      There are shells that do not reset the exit status from an `if':
 
           $ if (exit 42); then true; fi; echo $?
           42
 
      whereas a proper shell should have printed `0'.  This is especially
      bad in Makefiles since it produces false failures.  This is why
      properly written Makefiles, such as Automake's, have such hairy
      constructs:
 
           if test -f "$file"; then
             install "$file" "$dest"
           else
             :
           fi
 
 `pwd'
      With modern shells, plain `pwd' outputs a "logical" directory
      name, some of whose components may be symbolic links.  These
      directory names are in contrast to "physical" directory names,
      whose components are all directories.
 
      POSIX 1003.1-2001 requires that `pwd' must support the `-L'
      ("logical") and `-P' ("physical") options, with `-L' being the
      default.  However, traditional shells do not support these
      options, and their `pwd' command has the `-P' behavior.
 
      Portable scripts should assume neither option is supported, and
      should assume neither behavior is the default.  Also, on many hosts
      `/bin/pwd' is equivalent to `pwd -P', but POSIX does not require
      this behavior and portable scripts should not rely on it.
 
      Typically it's best to use plain `pwd'.  On modern hosts this
      outputs logical directory names, which have the following
      advantages:
 
         * Logical names are what the user specified.
 
         * Physical names may not be portable from one installation host
           to another due to network filesystem gymnastics.
 
         * On modern hosts `pwd -P' may fail due to lack of permissions
           to some parent directory, but plain `pwd' cannot fail for this
           reason.
 
      Also please see the discussion of the `cd' command.
 
 `set'
      This builtin faces the usual problem with arguments starting with a
      dash.  Modern shells such as Bash or Zsh understand `--' to specify
      the end of the options (any argument after `--' is a parameter,
      even `-x' for instance), but most shells simply stop the option
      processing as soon as a non-option argument is found.  Therefore,
      use `dummy' or simply `x' to end the option processing, and use
      `shift' to pop it out:
 
           set x $my_list; shift
 
      Some shells have the "opposite" problem of not recognizing all
      options (e.g., `set -e -x' assigns `-x' to the command line).  It
      is better to elide these:
 
           set -ex
 
 `shift'
      Not only is `shift'ing a bad idea when there is nothing left to
      shift, but in addition it is not portable: the shell of MIPS
      RISC/OS 4.52 refuses to do it.
 
 `source'
      This command is not portable, as POSIX does not require it; use
      `.' instead.
 
 `test'
      The `test' program is the way to perform many file and string
      tests.  It is often invoked by the alternate name `[', but using
      that name in Autoconf code is asking for trouble since it is an M4
      quote character.
 
      If you need to make multiple checks using `test', combine them with
      the shell operators `&&' and `||' instead of using the `test'
      operators `-a' and `-o'.  On System V, the precedence of `-a' and
      `-o' is wrong relative to the unary operators; consequently, POSIX
      does not specify them, so using them is nonportable.  If you
      combine `&&' and `||' in the same statement, keep in mind that
      they have equal precedence.
 
      You may use `!' with `test', but not with `if': `test ! -r foo ||
      exit 1'.
 
 `test' (files)
      To enable `configure' scripts to support cross-compilation, they
      shouldn't do anything that tests features of the build system
      instead of the host system.  But occasionally you may find it
      necessary to check whether some arbitrary file exists.  To do so,
      use `test -f' or `test -r'.  Do not use `test -x', because 4.3BSD
      does not have it.  Do not use `test -e' either, because Solaris
      2.5 does not have it.
 
 `test' (strings)
      Avoid `test "STRING"', in particular if STRING might start with a
      dash, since `test' might interpret its argument as an option
      (e.g., `STRING = "-n"').
 
      Contrary to a common belief, `test -n STRING' and `test -z STRING'
      *are* portable.  Nevertheless many shells (such as Solaris 2.5,
      AIX 3.2, UNICOS 10.0.0.6, Digital Unix 4 etc.) have bizarre
      precedence and may be confused if STRING looks like an operator:
 
           $ test -n =
           test: argument expected
 
      If there are risks, use `test "xSTRING" = x' or `test "xSTRING" !=
      x' instead.
 
      It is common to find variations of the following idiom:
 
           test -n "`echo $ac_feature | sed 's/[-a-zA-Z0-9_]//g'`" &&
             ACTION
 
      to take an action when a token matches a given pattern.  Such
      constructs should always be avoided by using:
 
           echo "$ac_feature" | grep '[^-a-zA-Z0-9_]' >/dev/null 2>&1 &&
             ACTION
 
      Use `case' where possible since it is faster, being a shell
      builtin:
 
           case $ac_feature in
             *[!-a-zA-Z0-9_]*) ACTION;;
           esac
 
      Alas, negated character classes are probably not portable,
      although no shell is known to not support the POSIX.2 syntax
      `[!...]' (when in interactive mode, `zsh' is confused by the
      `[!...]' syntax and looks for an event in its history because of
      `!').  Many shells do not support the alternative syntax `[^...]'
      (Solaris, Digital Unix, etc.).
 
      One solution can be:
 
           expr "$ac_feature" : '.*[^-a-zA-Z0-9_]' >/dev/null &&
             ACTION
 
      or better yet
 
           expr "x$ac_feature" : '.*[^-a-zA-Z0-9_]' >/dev/null &&
             ACTION
 
      `expr "XFOO" : "XBAR"' is more robust than `echo "XFOO" | grep
      "^XBAR"', because it avoids problems when `FOO' contains
      backslashes.
 
 `trap'
      It is safe to trap at least the signals 1, 2, 13, and 15.  You can
      also trap 0, i.e., have the `trap' run when the script ends
      (either via an explicit `exit', or the end of the script).
 
      Although POSIX is not absolutely clear on this point, it is widely
      admitted that when entering the trap `$?' should be set to the exit
      status of the last command run before the trap.  The ambiguity can
      be summarized as: "when the trap is launched by an `exit', what is
      the _last_ command run: that before `exit', or `exit' itself?"
 
      Bash considers `exit' to be the last command, while Zsh and
      Solaris 8 `sh' consider that when the trap is run it is _still_ in
      the `exit', hence it is the previous exit status that the trap
      receives:
 
           $ cat trap.sh
           trap 'echo $?' 0
           (exit 42); exit 0
           $ zsh trap.sh
           42
           $ bash trap.sh
           0
 
      The portable solution is then simple: when you want to `exit 42',
      run `(exit 42); exit 42', the first `exit' being used to set the
      exit status to 42 for Zsh, and the second to trigger the trap and
      pass 42 as exit status for Bash.
 
      The shell in FreeBSD 4.0 has the following bug: `$?' is reset to 0
      by empty lines if the code is inside `trap'.
 
           $ trap 'false
           
           echo $?' 0
           $ exit
           0
 
      Fortunately, this bug only affects `trap'.
 
 `true'
      Don't worry: as far as we know `true' is portable.  Nevertheless,
      it's not always a builtin (e.g., Bash 1.x), and the portable shell
      community tends to prefer using `:'.  This has a funny side
      effect: when asked whether `false' is more portable than `true'
      Alexandre Oliva answered:
 
           In a sense, yes, because if it doesn't exist, the shell will
           produce an exit status of failure, which is correct for
           `false', but not for `true'.
 
 `unset'
      You cannot assume the support of `unset'.  Nevertheless, because
      it is extremely useful to disable embarrassing variables such as
      `PS1', you can test for its existence and use it _provided_ you
      give a neutralizing value when `unset' is not supported:
 
           if (unset FOO) >/dev/null 2>&1; then
             unset=unset
           else
             unset=false
           fi
           $unset PS1 || PS1='$ '
 
       Special Shell Variables, for some neutralizing values.
      Also, see  Limitations of Builtins, documentation of
      `export', for the case of environment variables.
 
Info Catalog (autoconf.info.gz) Special Shell Variables (autoconf.info.gz) Portable Shell (autoconf.info.gz) Limitations of Usual Tools
automatically generated byinfo2html