How does the double parenthesis construct work in bash? - bash

I read here that double parenthesis allows for C-style variable manipulation. However, it did not work as expected when I tried to compare strings:
(( "a" == "b" )) && echo yes || echo no
# shows yes
I am also confused as to how this works for using variables as booleans. Following the example in the linked answer, I tried the following:
true=1
false=0
a=true
(( a )) && echo yes || echo no
# shows yes
a=false
(( a )) && echo yes || echo no
# shows no
But wouldn't a be a string value of either true or false?
In addition, since a "no error" value (0) is taken as true and any "error" value (non-zero) is taken as false in bash, why does it look like the opposite convention is taken here?

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.

Related

Nice way of assigning true or false to value in bash [duplicate]

Can someone explain how to perform Boolean operations and store them in variables in Bash?
I tried:
A=true
B=false
C=!$A
D=$A && $B
echo $C
echo $D
I also tried without dollars, with [], with {}, ()... How can one do such a simple operation in bash?
result in console are:
!true
true
It seems they are always treated as strings.
You deduced right, bash variables by default contain strings, and its values are treated as strings.
You can use the declare built-in command to explicitly say they store integers (declare -i myintvar), or indexed arrays (declare -a myarr), or associative arrays (declare -A mymap), etc., but not booleans.
The closest you can get to booleans is to use integer values 0 and 1 and evaluate expressions as arithmetic expressions with the (( expr )) command (bash-specific), or with arithmetic expansion $(( expr )) (POSIX-compatible). Those commands evaluate expr according to rules of shell arithmetic.
For example:
A=1
B=0
(( C = \!A )) # logical negation ==> C = 0
(( D = A && B )) # logical AND ==> D = 0
E=$(( A ^ B )) # bitwise XOR ==> E = 1
In bash, you can also use declare -i and let:
declare -i E='A||B' # equivalent to: E=$((A||B)), or ((E=A||B))
let C='!A' # equivalent to: C=$((\!A)), or ((C=\!A))
which are a longer way of saying ((..)) or $((..)). They both force arithmetic evaluation of the expressions given.
Note that ! has a special meaning in most shells (including bash), it causes history expansion. To prevent it, we must escape it with a backslash, or quote it.
Unfortunately, bash does not support boolean variables in a proper meaning. There is no "true" and "false" constants as in programming languages. Instead, /bin/true and /bin/false are two executables that don't do anything except return exit status 0 or 1. Contrary to common logic, exit status 0 is a synonim for "true" and 1 is a synonim for "false". The closest you can get to evaluating boolean expressions is either
[[ expr ]] which returns a exit status 0 or 1 depending on evaluating expr
&& and || (these are conditionals depending on last command's exit status)
[ which is actually an executable with a silly name (not part of bash) that supports some basic expressions
if...elif..else..fi which you can use to your advantage to manipulate variables within the workflow
like this?
t=true
f=false
# if $t; then echo Hi; fi
Hi
# if $f; then echo Hi; fi
# if ! $f; then echo Hi; fi
Hi
# if ! ($t && $t); then echo Hi; fi
# if ($t && $t); then echo Hi; fi
Hi

bash operators in if-statement

Im new to bash and can't understand how the operator quite work here.
The goal is to creating a script which ask user to input "y" to run the command or "n" to not run.
by using ==:
All inputs get passed such as (y, n, a, abc, etc..)
if (($TFENGINE_VAR==y))
by using =:
All inputs get rejected such as (y, n, a, abc, etc..)
if (($TFENGINE_VAR=y))
echo "run tfengine? (y/n)"
read TFENGINE_VAR
if (($TFENGINE_VAR==y))
then
echo "running tfengine command.."
sleep 1
tfengine --config_path=main.hcl --output_path=terraform/ -delete_unmanaged_files
else
echo "continue.."
continue
fi
Expressions surrounded with (( )) are evaluated as numeric expressions.
You need to use the test command, which returns 0 if test is successful, and a number different from 0 if not.
There are several options to the test command. In your case, you want to compare strings, so use each parameter surrounded with double quotes.
Also, when comparing strings you don't use == as the comparison operator.
if test "$TFENGINE_VAR" = "y"
then
...
Take a look at test man page: http://man.he.net/?topic=test&section=all
Your question is:
how the operator quite work here
The ((...)) is an arithmetic expression. Inside, all strings are interpreted as variables, and undefined variables are interpreted as equal 0. The exit status of ((...)) is non-zero if the expression is equal to zero, and the exit status is zero if the expression inside is equal to non-zero.
Because y variable is not defined, y is equal to 0. The (( $TFENGINE_VAR == y )) does the same as (( $TFENGINE_VAR == 0 )).
If you want to compare strings, you would use [[ or [ or test command.
All inputs get rejected such as
Sure, = is assignment inside arithmetic expression. If you input abc, then (( $TFENGINE_VAR = y )) becomes (( abc = 0 )) and it assigns the value 0 to variable abc. Note that variable expansion happens before the expansion of variables inside ((.
$ TFENGINE_VAR=abc
$ (($TFENGINE_VAR=y))
$ echo $abc
0
$ y=12345
$ (($TFENGINE_VAR=y))
$ echo $abc
12345
The result of assignment is equal to the value assigned, 0 in this case, so ((...)) exits with non-zero exit status, failure, so if branches to else part.

Subtraction and addition variables in bash script [duplicate]

for i in {1..99}
do
if ([ $((i % 2)) -eq 1 ])
then
echo $i
fi
done
I'm learning bash, and I'm trying to better understand line 3. Why does $((i % 2)) have to be double wrapped in parenthesis, and why can't I put the $ symbole inside next to the i like:
([ (($i % 2)) -eq 1 ])
or
([ ($(i % 2)) -eq 1 ])
?
Everything inside $((...)) is treated as an arithmetic expression. You can use parameter expansion inside an arithmetic expression, but a bare string is interpreted as a variable whose (integer) value is used. You can write
if [ $(( i % 2 )) -eq 1 ]
to check if i is odd. You can also check for equality inside the expression, as $(( x == y )) evaluates to 1 if x == y and 0 otherwise, but you would still have to compare that value to something.
In bash, you can use the arithmetic command, which has an exit status of 0 if the resulting value is non-zero, and 1 otherwise. This lets you write
if (( i % 2 == 1 )); then
$(( expression )) is the syntax for evaluating an arithmetic expression, and replacing this syntax with the result of that expression. It's documented in the Bash Manual here;
The syntax of arithmetic expressions is described here. Putting $ before variable names is optional, so you can also write it as $(($i % 2)).
You have to wrap it in two parentheses because $(...) already has a meaning, it's used for command substitution: $(some command) executes some command and is then replaced with the output of the command.
You don't need parentheses around [ ... ]. The normal way to write your if statement would be
if [ $((i % 2)) -eq 1 ]
You can also write it as
if (( i % 2 == 1 ))
(( expression )) evaluatees the arithmetic expression, and then sets its exit status depending on whether the result is zero or non-zero.
Since you specify bash, simplest is
for i in {1..99}
do if ((i % 2))
then echo $i
fi
done
the ((i % 2)) will return i mod 2, which will always be zero or one. This particular construct behaves like a C-style boolean, so zero is false and anything else is true (the opposite behavior from [[ ... ]] which uses return code of zero to mean true/ok).
You can also use expr:
for i in {1..99}
do
num=`expr i % 2`
if (( num == 1 ))
then
echo $i
fi
done

Could you explain the syntax of math in a bash shell?

for i in {1..99}
do
if ([ $((i % 2)) -eq 1 ])
then
echo $i
fi
done
I'm learning bash, and I'm trying to better understand line 3. Why does $((i % 2)) have to be double wrapped in parenthesis, and why can't I put the $ symbole inside next to the i like:
([ (($i % 2)) -eq 1 ])
or
([ ($(i % 2)) -eq 1 ])
?
Everything inside $((...)) is treated as an arithmetic expression. You can use parameter expansion inside an arithmetic expression, but a bare string is interpreted as a variable whose (integer) value is used. You can write
if [ $(( i % 2 )) -eq 1 ]
to check if i is odd. You can also check for equality inside the expression, as $(( x == y )) evaluates to 1 if x == y and 0 otherwise, but you would still have to compare that value to something.
In bash, you can use the arithmetic command, which has an exit status of 0 if the resulting value is non-zero, and 1 otherwise. This lets you write
if (( i % 2 == 1 )); then
$(( expression )) is the syntax for evaluating an arithmetic expression, and replacing this syntax with the result of that expression. It's documented in the Bash Manual here;
The syntax of arithmetic expressions is described here. Putting $ before variable names is optional, so you can also write it as $(($i % 2)).
You have to wrap it in two parentheses because $(...) already has a meaning, it's used for command substitution: $(some command) executes some command and is then replaced with the output of the command.
You don't need parentheses around [ ... ]. The normal way to write your if statement would be
if [ $((i % 2)) -eq 1 ]
You can also write it as
if (( i % 2 == 1 ))
(( expression )) evaluatees the arithmetic expression, and then sets its exit status depending on whether the result is zero or non-zero.
Since you specify bash, simplest is
for i in {1..99}
do if ((i % 2))
then echo $i
fi
done
the ((i % 2)) will return i mod 2, which will always be zero or one. This particular construct behaves like a C-style boolean, so zero is false and anything else is true (the opposite behavior from [[ ... ]] which uses return code of zero to mean true/ok).
You can also use expr:
for i in {1..99}
do
num=`expr i % 2`
if (( num == 1 ))
then
echo $i
fi
done

Using variables in Bash boolean expressions

Can someone explain how to perform Boolean operations and store them in variables in Bash?
I tried:
A=true
B=false
C=!$A
D=$A && $B
echo $C
echo $D
I also tried without dollars, with [], with {}, ()... How can one do such a simple operation in bash?
result in console are:
!true
true
It seems they are always treated as strings.
You deduced right, bash variables by default contain strings, and its values are treated as strings.
You can use the declare built-in command to explicitly say they store integers (declare -i myintvar), or indexed arrays (declare -a myarr), or associative arrays (declare -A mymap), etc., but not booleans.
The closest you can get to booleans is to use integer values 0 and 1 and evaluate expressions as arithmetic expressions with the (( expr )) command (bash-specific), or with arithmetic expansion $(( expr )) (POSIX-compatible). Those commands evaluate expr according to rules of shell arithmetic.
For example:
A=1
B=0
(( C = \!A )) # logical negation ==> C = 0
(( D = A && B )) # logical AND ==> D = 0
E=$(( A ^ B )) # bitwise XOR ==> E = 1
In bash, you can also use declare -i and let:
declare -i E='A||B' # equivalent to: E=$((A||B)), or ((E=A||B))
let C='!A' # equivalent to: C=$((\!A)), or ((C=\!A))
which are a longer way of saying ((..)) or $((..)). They both force arithmetic evaluation of the expressions given.
Note that ! has a special meaning in most shells (including bash), it causes history expansion. To prevent it, we must escape it with a backslash, or quote it.
Unfortunately, bash does not support boolean variables in a proper meaning. There is no "true" and "false" constants as in programming languages. Instead, /bin/true and /bin/false are two executables that don't do anything except return exit status 0 or 1. Contrary to common logic, exit status 0 is a synonim for "true" and 1 is a synonim for "false". The closest you can get to evaluating boolean expressions is either
[[ expr ]] which returns a exit status 0 or 1 depending on evaluating expr
&& and || (these are conditionals depending on last command's exit status)
[ which is actually an executable with a silly name (not part of bash) that supports some basic expressions
if...elif..else..fi which you can use to your advantage to manipulate variables within the workflow
like this?
t=true
f=false
# if $t; then echo Hi; fi
Hi
# if $f; then echo Hi; fi
# if ! $f; then echo Hi; fi
Hi
# if ! ($t && $t); then echo Hi; fi
# if ($t && $t); then echo Hi; fi
Hi

Resources