Newsgroups: comp.unix.shell
From: Stephane CHAZELAS 
Subject: Re: how to write portable shell scripts?
References: >1120030541.202806.109050@g43g2000cwa.googlegroups.com<
Message-ID: >slrndc4o2s.6fu.stephane.chazelas@spam.is.invalid<
Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: 8bit
User-Agent: slrn/0.9.8.1 (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
(http://heirloom.sourceforge.net/sh.html).

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
(http://www.opengroup.org/onlinepubs/009695399/toc.htm) 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
    incompatibilities)
  - head/tail (-12 vs -n 12)
  - id (don't rely on any of the options nor on the output
    format).
  - 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
    format).
  - 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
    status.
  - 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.

--
Stéphane