I've been in correspondence with a few people who still seem to believe
that AT&T ksh88 is POSIX compliant because POSIX was originally based on
ksh88. On Solaris, to this day, /usr/xpg4/bin/sh is ksh88 and is
considered "the POSIX compliant shell".
While developing modernish (my cross-platform shell language extension
and feature testing library), I've not only found that POSIX
intentionally deviates from ksh88 in at least three important ways (see
FTL_NOFNOVER, FTL_PARONEARG and FTL_UPP below), but ksh88 also has many
serious bugs, some of which have fatal effects. It is also no longer
being developed, and closed source, so these bugs will never be fixed. I
believe the following shows that ksh88 should not actually be considered
compliant.
/usr/xpg4/bin/sh on Solaris 11.3 has at least the following bugs. The
list is not exhaustive; this is just what I've discovered while
developing modernish. The IDs here are those from modernish's shell
bug/quirk/feature testing framework. FTL_* are considered "fatal" shell
bugs that will normally cause modernish to refuse to initialise.
* FTL_ASGNBIERR: Variable assignments preceding regular builtin commands
should not persist after the command exits, but with this bug they do if
the builtin exits with an error. This may break scripts in obscure ways.
For example, 'LC_ALL=C test a -wrong b' causes the LC_ALL assignment to
persist, so your locale settings are gone. Even more havoc may be
wreaked with a similar temporary assignment to PATH...
* FTL_BRACSQBR: a bracket glob pattern containing a quoted closing
square bracket ']' is never matched, not even if that character is
escaped or passed from a quoted variable. For example,
case ] in [a\]b] ) echo ok;; * ) echo bug;; esac
case a in [a\]b] ) echo ok;; * ) echo bug;; esac
case b in [a\]b] ) echo ok;; * ) echo bug;; esac
all output 'bug'.
* FTL_COMMAND2P: double parsing of parameter expansion when using
'command' with an external command. For example,
a=\$x
x=bug
command printf '%s\n' "$a"
prints 'bug'. Clearly, this makes the 'command' builtin unusable.
* FTL_NOFNOVER: ksh88 does not allow you to override a regular shell
builtins with a shell function. POSIX specifies that only special
builtins can't be overridden with shell functions, and all other shells
allow overriding regular builtins.
* FTL_PARONEARG: When IFS is empty (i.e. field splitting is off), "$@"
is counted as a single argument instead of each positional parameter as
separate arguments. In other words, "$@" does not *generate* fields when
field *splitting* is disabled (which is of course illogical and POSIX
has rightly fixed this).
* FTL_UNSETFAIL: the 'unset' command sets a non-zero exit status if the
variable to unset was not already set, whereas POSIX says: "Unsetting a
variable or function that was not previously set shall not be considered
an error [...]"
This can cause spurious non-zero exit statuses for functions that end in
an 'unset' command to clean up internal variables. (In itself this is
easy to work around; modernish only considers this bug fatal because all
existing shells that have this bug have other fatal errors as well.)
* FTL_UPP: Cannot access "$@" or "$*" if set -u (-o nounset) is active
and there are no positional parameters. If that option is set, ksh88
errors out on accessing "$@" and "$*". This makes 'set -u' impractical
to use, so POSIX changed this from ksh88, rendering "$@" and "$*" exempt
from 'set -u'.
* BUG_ARITHINIT: Using unset or empty variables in arithmetic
expressions causes the shell to error out with a "bad number" error.
Instead, according to POSIX, it should take them as a value of zero.
* BUG_CMDPV: More 'command' builtin breakage: the -p and -v options
cannot be combined, so you can't query the location of the command
within the default system PATH.
* BUG_HDPARQUOT: Quotes within parameter substitutions in Here-Documents
aren't removed. For instance, if 'var' is set, ${var+"x"} in a here-
document erroneously yields "x", not x.
* BUG_PP_08: When IFS is null, unquoted $* within a substitution (e.g.
${1+$*} or ${var-$*}) does not generate one field for each positional
parameter as expected, but instead joins them into a single field.
* BUG_PSUBBKSL: A backslash-escaped character within a quoted parameter
substitution is not unescaped, e.g.
echo "${foo-\x}"
outputs '\x' instead of 'x'.
That's all I have. Maybe some of you know some other ksh88 bugs...
- Martijn
P.S. Not that it's very relevant anymore, but for completeness's sake:
/usr/bin/ksh on Solaris 10.1 (non-sh ksh88, which was replaced by ksh93
on Solaris 11) has the following bugs as well:
* FTL_NOARITH: incomplete and buggy support for POSIX shell arithmetic.
Particularly, octal numbers by leading zero such as $((010 == 8)) are
not supported. There are other bugs as well, like:
x=42
echo $(( (y=x)==0x2A ))
results in "0x2A: bad number", which is clearly wrong, because
echo $(( (y=42)==0x2A ))
works.
* BUG_CSCMTQUOT: unbalanced single and double quotes and backticks in
comments within $(command substitutions) cause obscure and hard-to-trace
syntax errors later on in the script. Example:
echo $(echo one # this won't work
echo two )
spuriously parses the "'" in the comment as shell grammar, causing an
unmatched quote error that may or may not show up as such, depending on
the script.
* BUG_CSNHDBKSL: Backslashes within non-expanding here-documents (that
is, with quoted end-of-document delimiters) within command substitutions
are incorrectly expanded to perform newline joining, as opposed to left
intact. For example,
echo "$(cat <<'EOF'
abc
def \
ghi
EOF
)"
outputs:
abc
def ghi
instead of:
abc
def \
ghi