Today we ran into an interesting bug in one of our internal shell scripts. The script had worked for years on our Solaris 10 machines, but on a new OmniOS fileserver it suddenly reported an error:
script[77]: [: 232G: arithmetic syntax error
Cognoscenti of ksh error messages have probably already recognized this one and can tell me the exact problem. To show it to everyone else, here is line 77:
if [ "$qsize" -eq "none" ]; then ....
In a strict POSIX shell, this is an error because
test
's
-eq
operator is specifically for comparing numbers , not strings. What we wanted is the
=
operator.
What makes this error more interesting is that the script had been running for some time on the OmniOS fileserver without this error. However, until now the
$qsize
variable had always had the value '
none
'. So why hadn't it failed earlier? After all, '
none
' (on either side of the expression) is just as much of not-a-number as '
232G
' is.
The answer is that this is a picky difference between shells in terms of how they actually behave. Bash, for example, always complains about such misuse of
-eq
; if either side is not a number you get an error saying '
integer expression expected
' (as does Dash, with a slightly different error). But on our OmniOS,
/bin/sh
is actually ksh93 and ksh93 has a slightly different behavior. Here:
$ [ "none" -eq "none" ] && echo yes yes $ [ "bogus" -eq "none" ] && echo yes yes $ [ "none" -eq 0 ] && echo yes yes $ [ "none" -eq "232G" ] && echo yes /bin/sh: [: 232G: arithmetic syntax error
The OmniOS version of ksh93 clearly has some sort of heuristic about number conversions such that strings with no numbers are silently interpreted as '0'. Only invalid numbers (as opposed to things that aren't numbers at all) produce the 'arithmetic syntax error' message. Bash and dash are both more straightforward about things (as is the FreeBSD
/bin/sh
, which is derived from ash).
Update: my description isn't actually what ksh93 is doing here; per opk's comment, it's actually interpreting the
none
and
bogus
as variable names and giving them a value of 0 when unset.
Interestingly, the old Solaris 10
/bin/sh
seems to basically be calling
atoi()
on the arguments for
-eq
; the first three examples work the same, the fourth is silently false, and '
[ 232 -eq 232G
]
' is true. This matches the 'let's just do it' simple philosophy of the original Bourne shell and
test
program and may be authentic original V7 behavior.
(Technically this is a difference in
test
behavior, but
test
is a builtin in basically all Bourne shells these days. Sometimes the standalone
test
program in
/bin
or
/usr/bin
is actually a shell script to invoke the builtin.)