Chapter 11. Portable Shell Programming

When writing your own checks, there are some shell-script programming techniques you should avoid in order to make your code portable. The Bourne shell and upward-compatible shells like the Korn shell and Bash have evolved over the years, but to prevent trouble, do not take advantage of features that were added after unix version 7, circa 1977 (the section called “Systemology”).

You should not use shell functions, aliases, negated character classes, or other features that are not found in all Bourne-compatible shells; restrict yourself to the lowest common denominator. Even unset is not supported by all shells! Also, include a space after the exclamation point in interpreter specifications, like this:

#! /usr/bin/perl

If you omit the space before the path, then 4.2bsd based systems (such as Sequent DYNIX) will ignore the line, because they interpret #! / as a 4-byte magic number. Some old systems have quite small limits on the length of the #! line too, for instance 32 bytes (not including the newline) on SunOS 4.

The set of external programs you should run in a configure script is fairly small. , for the list. This restriction allows users to start out with a fairly small set of programs and build the rest, avoiding too many interdependencies between packages.

Some of these external utilities have a portable subset of features; see the section called “Limitations of Usual Tools”.

There are other sources of documentation about shells. See for instance the Shell FAQs[18].

Shellology

There are several families of shells, most prominently the Bourne family and the C shell family which are deeply incompatible. If you want to write portable shell scripts, avoid members of the C shell family. The the Shell difference FAQ[19] includes a small history of Unix shells, and a comparison between several of them.

Below we describe some of the members of the Bourne shell family.

Ash

ash is often used on gnu/Linux and bsd systems as a light-weight Bourne-compatible shell. Ash 0.2 has some bugs that are fixed in the 0.3.x series, but portable shell scripts should workaround them, since version 0.2 is still shipped with many gnu/Linux distributions.

To be compatible with Ash 0.2:

  • don't use $? after expanding empty or unset variables:

    foo=
    false
    $foo
    echo "Don't use it: $?"
    
  • don't use command substitution within variable expansion:

    cat ${FOO=`bar`}
    
  • beware that single builtin substitutions are not performed by a sub shell, hence their effect applies to the current shell! the section called “Shell Substitutions ”, item "Command Substitution".

Bash

To detect whether you are running bash, test if BASH_VERSION is set. To disable its extensions and require posix compatibility, run set -o posix. , for details.

Bash 2.05 and later

Versions 2.05 and later of bash use a different format for the output of the set builtin, designed to make evaluating this output easier. However, this output is not compatible with earlier versions of bash (or with many other shells, probably). So if you use bash 2.05 or higher to execute configure, you'll need to use bash 2.05 for all other build tasks as well.

/usr/xpg4/bin/sh on Solaris

The posix-compliant Bourne shell on a Solaris system is /usr/xpg4/bin/sh and is part of an extra optional package. There is no extra charge for this package, but it is also not part of a minimal OS install and therefore some folks may not have it.

Zsh

To detect whether you are running zsh, test if ZSH_VERSION is set. By default zsh is not compatible with the Bourne shell: you have to run emulate sh and set NULLCMD to :. , for details.

Zsh 3.0.8 is the native /bin/sh on Mac OS X 10.0.3.

The following discussion between Russ Allbery and Robert Lipe is worth reading:

Russ Allbery:

The gnu assumption that /bin/sh is the one and only shell leads to a permanent deadlock. Vendors don't want to break users' existing shell scripts, and there are some corner cases in the Bourne shell that are not completely compatible with a posix shell. Thus, vendors who have taken this route will never (OK…"never say never") replace the Bourne shell (as /bin/sh) with a posix shell.

Robert Lipe:

This is exactly the problem. While most (at least most System V's) do have a Bourne shell that accepts shell functions most vendor /bin/sh programs are not the posix shell.

So while most modern systems do have a shell somewhere that meets the posix standard, the challenge is to find it.



[18] the Shell FAQs, http://www.faqs.org/faqs/unix-faq/shell/.

[19] the Shell difference FAQ, http://www.faqs.org/faqs/unix-faq/shell/shell-differences/.