The main thing to take note of is that the double-parenthesis construct allows for arithmetic evaluation and expansion, and is not an inline C interpreter. Thus, only the rules defined in Shell Arithmetic applies, that is, only C operations on integer types work in the double parentheses.
First example: bash constructs expand first
Anything outside the arithmetic operators are expanded first according to bash rules, e.g. quotes, parameter expansion, bash range {1..5}
and list{a,b}
constructs, before the double parentheses evaluation starts.
In the first example, double quotes cause what's inside to be interpreted as a single word (no effect inside double-paren) and also evaluate things starting with $ (but there's none inside the quotes), so the first example simply becomes (( a == b ))
.
Thus, the best way to understand how (( ))
works is to work out all the bash constructs first in your mind, then plugging it in. You can also write examples to test your assumptions.
Example with parameter expansion taking place:
a=1
b=2
(( a = $b )) # becomes (( a = 2 ))
(( a = b )) # straight arithmetic evaluation of a = b within the double parenthesis
# they produce the same result but how they arrive at the result is different
(( $a = b )) # becomes (( 2 = b ))
# syntax error as that's not a valid expression.
Notes
There are some peculiarities when you compare the closely related $(( ))
and (( ))
constructs. The former (Arithmetic Expansion) treats the expression as if the expression is within double quotes, while the latter does not, as explained above.
Second example: Variables in the rvalue position expand recursively
There are some subtle rules in Shell Arithmetic:
- "The value of a variable is evaluated as an arithmetic expression when it is referenced"
- "or when a variable which has been given the integer attribute using
declare -i
is assigned a value".
- "A shell variable that is null or unset evaluates to 0 when referenced by name without using the parameter expansion syntax"
After trying it out for a bit, you will see that this basically means that any variables in the rvalue will be evaluated recursively until it reaches a value that is either an integer, or is an undefined/null variable name:
b=c
c=d
(( a = b ))
echo $a
# gives 0
d=3
(( a = b ))
echo $a
# gives 3
unset d
declare -i a
a=b
echo $a
# gives 0
d=3
a=b
echo $a
# gives 3
You can also play tricks with putting expressions in variables and evaluating it later:
b=2
c=3
d=c
e=b+d
(( a = e ))
echo $a
# gives 5, as it unfolds like a=b+d; a=2+c; a=2+3
So in the example in the question, a
evaluated to true
, then evaluated to 1 to give the final result.
How does (( ))
reverse the interpretation for true and false
(( 0 ))
echo $? # print the return code of the previous call
# prints 1, which indicates error/false in shell
(( 1 ))
echo $?
# prints 0, indicating success/true
(( 2 ))
echo $?
# prints 0
(( -1 ))
echo $?
# prints 0
So the behaviour inside the parentheses is consistent with the C interpretation for true and false, with 0 indicating false and non-zero indicating true. (( ))
"converts" false to a return value of 1 and true to a return value of 0.