From: Stephane CHAZELAS 
Subject: Re: how to write portable shell scripts?
References: ><
Message-ID: ><
Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: 8bit
User-Agent: slrn/ (Debian)
Date: Wed, 29 Jun 2005 09:48:28 +0100

2005-06-29, 00:35(-07), Ralph A. Moeritz:
> I'd like to make my scripts as portable as possible, since I fear
> they may have to run on scary systems like an old SCO OpenServer
> box. I have tried to restrict myself to features described in the
> FreeBSD-5.4 sh(1) manpage, which claims to be (mostly) POSIX.2
> compliant.
> Does anybody have some advice on portability, or common traps,
> caveats & pitfalls for certain shells etc? The point is that I
> may not be the administrator of the machine in question, in
> which case I can't simply install bash :-(

There are some useful hints in the autoconf documentation.
FreeBSD sh is not the Bourne shell, it's POSIX conformant (sort
of). You may need Bourne compatibility. /bin/sh on Solaris is
one, but as its code has just been released, you can download it
and compile it now for Linux

Your biggest problems will probably be on the utilities. Never
rely on GNU documentation as their tools have plenty of
non-standard features. You may want first to try to be POSIX
conformant (except for the shell syntax as you want Bourne
compatibility) as there's a text you can rely on
( and then
try to fix the problems for non-POSIX systems (Solaris is
supposed to be POSIX, but it is not by default, so you can
consider it as a non-POSIX system as far as portable shell
scripting is concerned).

compatibility issues I know about (I'm not taking the
non-standard GNU features into account) are:
  - echo (the \t \n, \c, the -e/-n options)
  - awk (two main variants: the old and new one, and a third:
    the POSIX standard) and even then various incompatibilities
    (see shelldorado)
  - basename (the second argument is sometimes a regexp)
  - cat (for its options)
  - cd/pwd (no -P -L in some shells)
  - command (not in Bourne)
  - bc/dc: various problems
  - dd (iseek vs skip)
  - export (has a special command line parsing in some shells
    like bash). Anyway for Bourne, you need
    var=value; export var (or var=value export var)
  - dirname (some bugs)
  - find, plenty of issues and bugs
  - getopts not always there
  - grep (egrep vs grep -E, issues with -q/-s, regexp
  - head/tail (-12 vs -n 12)
  - id (don't rely on any of the options nor on the output
  - kill, trap (may not recognize the signal names and the
    signal numbers are different from one shell to another)
  - ln (don't rely on -sf or -fs)
  - ls (don't rely on the output format, problems with the -g
    option and more)
  - mkdir (not sure about this but I think not all versions
    recognize -p)
  - mkfifo: not all systems support fifos. some need mknod p
  - od (two variants with different option sets)
  - tar (no compatibility for the options nor the produced
  - printf (not in very old systems, problems with %c, with \xxx)
  - ps (different variants)
  - read (-r not portable, different behaviors wrt IFS)
  - return (different behaviors for sourced files)
  - "." (wrt to arguments)
  - sed: see the sed FAQ
  - set: set -- with no arguments different behaviors
    hence the set x ...; shift instead.
  - sh -c 'inline-script' foo bar, and foo being either in $0
    or $1
  - sort (-k 1 vs +0)
  - stty: cbreak/-icanon
  - test: -h/-e/-L non-portable. Don't pass it more that 3
    arguments. For three arguments, use "x$a" = "x$b".
    [ 010 -gt 9 ] is sometimes false as 010 is octal for 8.
  - touch: different ways to give the time.
  - tr: \12 vs \n, [a-z] vs a-z...
  - trap: ensure the first argument don't look like a number of
    a signame. don't rely on the trap on 0/EXIT
  - type: no standard output format, you can't rely on the exit
  - unset not always there.
  - xargs: input format syntax different from implementation to
    implementations (wrt quoting)

  - "--" to separate options from arguments not recognized on
    old system utilities, you have to ensure the arguments don't
    look like options
  - TZ, LC_*/LANG and various other environment variables have
    an influence on some utility behavior
  - many bugs in many shells (especially the ksh based) you may
    have to work around.
  - don't try to do anything fancy with IFS
  - no trap in functions.
  - some ksh optimisations have nasty side effects
  - use a newline rather that a ";" before then/do.
  - when using backtiks (`...`) use intermediate variables as
    var=`...`; cmd "$var".
  - "$@" when there's no argument resolves to 1 empty argument
    rather than no arguments. use ${1+"$@"} to work around (that
    doesn't work for zsh based sh, see autoconf doc)
  - a redirection or pipe always involves a subshell in Bourne
    (except for "exec").
  - no function support in very very old shells.

That's far from being exhaustive, I'm afraid.