When the shell did the arithmetic anyway
- #bug-fix
- #arithmetic
- #bash-compat
Shell arithmetic has a quiet rule: by the time an expression is evaluated, every
$ in it has already been substituted. So a $ you still see in the expression
is one you deliberately protected — with a backslash, or with quotes — and bash
refuses to expand it a second time.
huck expanded it anyway. The worst shape was a test:
# before
$ x=5
$ [[ '$x' -eq 5 ]]; echo "rc=$?"
rc=0# after
$ x=5
$ [[ '$x' -eq 5 ]]; echo "rc=$?"
huck: line 1: [[: $x: syntax error: operand expected (error token is "$x")
rc=1The quotes were there to compare the literal text $x. huck read the variable
instead, so the test silently passed. The same happened inside an expression:
# before
$ x=5; echo $(( 1 + "\$x" ))
6# after
$ x=5; echo $(( 1 + "\$x" ))
huck: line 1: 1 + $x : syntax error: operand expected (error token is "$x ")Integer variables that swallowed their errors
An integer-flagged variable evaluates whatever you assign to it. When that failed, huck said nothing and stored zero:
# before
$ declare -i total=99
$ total=$(echo 1.5)
$ echo "total=$total"
total=0# after
$ declare -i total=99
$ total=$(echo 1.5)
huck: line 1: 1.5: syntax error: invalid arithmetic operator (error token is ".5")
$ echo "total=$total"
total=99A script reading a number from somewhere could not tell a real zero from a value
it failed to parse. The += form had the same hole, in a different place:
declare -i n=5; n+=@ quietly left n at 5.
Saying which part failed
When arithmetic did fail inside a test or a for loop header, huck named the
problem but not the expression:
# before
$ [[ @ -eq 5 ]]; echo "rc=$?"
huck: line 1: [[: syntax error: operand expected
rc=2# after
$ [[ @ -eq 5 ]]; echo "rc=$?"
huck: line 1: [[: @: syntax error: operand expected (error token is "@")
rc=1The exit status was wrong too — bash treats a broken comparison as a false-with-error (1), not a usage error (2).
Still open
This pass filed eight neighbouring issues. The one most worth knowing about:
a failed operand still abandons the whole test, where bash reports it and keeps
going, so [[ @ -eq 5 || 1 -eq 1 ]] is true in bash and false in huck
(#718).
Also open: a quoted array subscript is still evaluated (#699), an unusable regex prints the underlying library's error where bash is silent (#716), an unterminated bracket in a pattern errors where bash treats it as a literal (#717), and a number with trailing junk gets the wrong message (#720).