This script:
a=2
[[ "$a" -eq 2 ]] && echo yes1
[[ $a -eq 2 ]] && echo yes2
[[ a -eq 2 ]] && echo yes3
[[ "a" -eq 2 ]] && echo yes4
Does not work in dash (and some others that do not have [[ ).
Fails the last two "yes tests" in ash (BusyBox ash). As I expected.
But, quite UN expectly:
This happens when the arithmetic » -eq « is being used (and some other).
Prints all the four yes in ksh, ksh93, lksh, mksh, bash and zsh.
Q's
Is this the natural side effect of "Arithmetic Expansion" being applied to labels like (a
) ?.
(nothing to see, move along ?)
Should (a
) still be expanded as a variable when quoted: "a"
?
Is this documented somewhere I haven't look into yet?
Or, Is a simple (very old) bug?
Edit
To really get you thinking, check this:
a=2; b=2
[[ "$a" -eq "$b" ]] && echo yes1
[[ $a -eq "$b" ]] && echo yes2
[[ a -eq "$b" ]] && echo yes3
[[ "a" -eq "$b" ]] && echo yes4
[[ "$a" -eq "$b" && "$a" == "$b" ]] && echo new1
[[ $a -eq "$b" && $a == "$b" ]] && echo new2
[[ a -eq "$b" && a == "$b" ]] && echo new3
[[ "a" -eq "$b" && "a" == "$b" ]] && echo new4
All yes work, only new1 and new2 do. The var a is not being expanded in new3 & 4.
That means that shells switch mode (Arithmetic — String) in the middle of the evaluation.
This doesn't seem so obvious to me. At least, is not simple, IMO.
Note that ash does it in a different way. And also report the error.
Conclusion:
I do not know the correct answer. But I suspect (looking at so many shells doing it exactly the same way) that it is a very well known way (to developers) in which this tests should work. It should be up to us to understand the fact and adapt.
Seems that for numeric -eq
the only possible 'Conditional Expression' to apply is:
exp1 -eq exp2
true if exp1 is numerically equal to exp2.
Which makes both sides of the -eq
"Arithmetic expressions"
While for the ==
the only possible 'Conditional Expression' to apply is:
string == pattern
true if string matches pattern. ....
Which makes the left hand side an string (no expansion?) and the right hand side a pattern (which really do not apply to the test I presented as all were quoted on the right side to avoid this complexity. So, right hand side defaults (being quoted) to an string also (no arithmetic expansion here).
But maybe I am more confused than I though 🙂
What do you say?
Best Answer
From
man ksh
:The documentation there is consistent where references to arithmetic expressions are concerned, and (apparently carefully) avoids any self-contradictions surrounding the definition of the
[[
compound command]]
pertaining to string comparison by explicitly also permitting some obsolete arithmetic comparisons in the same context.From
man bash
:I think, given all that context, the behavior you observe stands to reason, even if it is not explicitly spelled out as a possibility in the documentation there. The docs do point to special treatment of parameters with integer attributes, and clearly denote a difference between a compound command and a builtin command.
The
[[
comparison is syntax in the same sense that the assignmentname
=
value
is syntax orcase
word
in...
is syntax.test
and[
, however, are not as such, and are rather separate procedures which take arguments. As I think, the best way to really get a feel for the differences is to have a look at shell error output:The two shells handle the exceptions differently, but the underlying reasons for the differences in both cases for both shells are very similar.
bash
directly calls the[[ \\
case a syntax error - in the same way it might for a redirect from a non-existent file, for example - though it goes on from that point (as I believe, incorrectly) to evaluate the other side of the||
or expression.bash
does give the[[
expression a command name in error output, but note that it doesn't bother discussing the line number on which you call it as it does for the[
command.bash
's[
complains about not receiving what it expects to be an integer expression as an argument, but[[
need not complain in that way because it doesn't really take arguments, and never needs to expect anything at all when it is parsed alongside the expansions themselves.ksh
halts altogether when the[[
syntax error and doesn't bother with[
at all. It writes the same error message for both, but note that[
is assigned a command name there where[[
is justksh
. The[
is only called after the command-line has been successfully parsed and expansions have already occurred - it will do its own littlegetopts
routine and get its ownarg[0c]
and the rest, but[[
is handled as underlying shell syntax once again.I consider the
bash
docs slightly less clear than theksh
version in that they use the termsarg[12]
rather thanexpression
regarding integer comparisons, but I think it is done merely because[[
,[
, andtest
are all lumped together at that juncture, and the latter two do take arguments whereas the former only ever receives anexpression
.In any case, where the integer comparison is not ambiguous in the syntax context, you can basically do any valid math operation mid-
expression
: