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

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

Related

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

How does the double parenthesis construct work in 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.

Why do the following string comparisons return different results?

What is the difference between the following commands and why do they not produce the same output?:
echo $[ "a" == "a" ]
1
echo $(( "a" == "a" ))
1
[ "a" == "a" ]; echo $?;
0
To me, these seem like the same operations but the first two which are appended with $ return false...
That's incorrect. The first two expressions are arithmetic expressions. They produce a numeric result. As is common in many languages, a boolean True evaluates as the integer value 1, and False evaluates as 0.
Note that $[...] and $((...)) are equivalent, but the former is deprecated and is no longer documented.
...while the third returns true
This isn't true either. Here you are looking at the return code of a command, rather than printing out the value of an expression as you are doing in the first two examples. Commands don't return boolean information; they return an integer indicating whether they were successful (a return code of 0) or not (any non-zero return code).
Regarding your question, the false command doesn't return "false"; if you read the man page, it says:
false - do nothing, unsuccessfully
That is, it is a command that does nothing and returns an error code (i.e., a nonzero exit code, in this case, 1). true is similar, of course, except it returns a successful exit code (i.e., 0).
echo $[ "a" == "a" ]
uses the deprecated integer expansion syntax, which has been replaced by $(( ... )).
echo $(( "a" == "a" ))
uses what is called arithmetic expansion: inside $(( ... )), you can have logical and numerical expressions that get calculated before returning the value.
[ "a" == "a" ]; echo $?
does a logical test in standard bash fashion: the return code of a program is 0 when that program executes successfully, and non-zero otherwise. [ is actually another name for the program called test, which returns success (i.e., 0) when the expression it tests is true.
Not in your list, but related:
(( "a" == "a" )); echo $?
0
this does arithmetic expansion, and returns success (0) if the result is true, rather than producing the value of the expression itself.
[[ "a" == "a" ]]; echo $?
0
[[ ... ]] is bash syntax for doing conditional tests directly instead of calling the program test.
Some sample usages
You would use $(( ... )) to initialize variables or echo output, as you did:
foo=$(( 1 + 4))
echo $(( 5 + 7 ))
You would use [ ... ], (( ... )) and [[ ... ]] in conditional statements:
if [[ $a == bar ]]; then
echo $a is bar
fi
i=0
while (( i < 10 )); do
echo i=$i
i=$(( i + 1 ))
done
Your first two perform arithmetic comparisons (never use $[...]; it's undocumented, obsolete, and was replaced by $((...))—literally—decades ago). In an arithmetic context, strings are treated as implicit parameter expansions, recursively, until an integer is produces. Undefined parameters are treated as 0. The resulting expression has value 1 if the comparison is true, 0 if false. Compare:
$ echo $(( a == b )) # Compares 0 == 0
1
$ a=5; b=7
$ echo $(( a == b )) # Compares 5 == 7
0
The last one performs string comparison, as expected. The exit status of [ ... ] is 0 when the comparison is true, 1 when false. (This is the difference between the value of a Boolean expression, and the result of a command that evaluates a Boolean expression.) Integer comparisons are made using the -eq operator.
$ [ a = a ]; echo $? # String comparison of the letter a to itself
0
$ [ a = b ]; echo $? # String comparison of the letter a to the letter b
1
$ unset a b
$ [ a -eq b ]; echo $? # Again, 0 -eq 0
0
$ a=5; b=7
$ [ "$a" -eq "$b" ]; echo $? # Again, 5 -eq 7
1

Integer expression expected BASH scripting IF statement

I am trying to insert names and numbers in a text file. I have wrote a short script for the learning purpose.
v= expr $# % 2
echo $v
if [ "$v" -eq 0 ]; then
i=1
while [ "$i" -lt $# ]
do
echo "$i $i+1" >> database
i=$((i+1))
done
echo "User(s) successfully added \n\v"
else
echo "Arguments are not complete";
fi
When i enter two arguments, the shell output is as follows
0 # (The value of variable v)
./myscript: line 3: [: : integer expression expected
Arguments are not complete # (else statement is executed)
When i replace -eq to == in line 3 (if statement), error msg is gone but still the IF statement doesn't execute as i expect.
0 # (output of variable v)
Arguments are not complete # (else statement is executed)
You need to enclose the variable assignment in $(...) ("command substitution"):
v=$(expr $# % 2)
In the if statement, -eq should be correct. Also, to make sure it works, I would use double square brackets (this might depend on the shell you use):
if [[ ${v} -eq 0 ]]; then
The immediate problem is the failure to use command substitution to capture the result of the expr command: v=$( expr $# % 2 ). However,
expr is no longer needed for arithmetic; use an arithmetic expression just as you did to increment i.
v=$(( $# % 2 ))

How to learn if a value is even or odd in bash?

I am building a movie database and I need to find a median for ratings.
I'm really new to bash (it's my first assignment).
I wrote:
let evencheck=$"(($Amount_of_movies-$Amount_of_0_movies)%2)"
if [ $evencheck==0 ]
then
let median="(($Amount_of_movies-$Amount_of_0_movies)/2)"
else
let median="(($Amount_of_movies-$Amount_of_0_movies)/2)+1"
fi
When $amount_of_movies = 6 and $amount_of_0_movies = 1. I would expect median to be 3. but it's 2. Why is that?
#!/bin/bash
if (( $# != 1 )) ; then
echo "syntax: `basename $0` number"
exit 255
else
_value=$(expr $1 % 2)
(( $_value == 0 )) && exit 1 || exit 0
fi
try this:
let evencheck="(($Amount_of_movies-$Amount_of_0_movies)%2)"
if [ $evencheck -eq 0 ]
then
let median="(($Amount_of_movies-$Amount_of_0_movies)/2)"
else
let median="(($Amount_of_movies-$Amount_of_0_movies)/2)+1"
fi
Removing the $, and then testing for numeric equality.
try this in your if test:
[ "$evencheck" == 0 ]
Does median have some sort of default value? The way I see it, it's not even going inside the if.
(6-1) % 2
5 % 2
1, not 0
Your code doesn't parse. To evaluate expressions in Bash, you say
let evencheck="$((($Amount_of_movies-$Amount_of_0_movies)%2))"
That is, evaluate arithmetics like so: $(( ... ))
That said, your problem is in the conditional. See man test.
if [ $evencheck = 0 ]; then
You have to wrap the equality sign with whitespace on both sides. Use a single equality sign.
The outermost parentheses and none of the quotes are necessary in what you've got (they may be in other circumstances). Also, in a let statement you can omit the dollar sign from variable names.
let evencheck=(Amount_of_movies-Amount_of_0_movies)%2
if [ $evencheck == 0 ]
then
let median=(Amount_of_movies-Amount_of_0_movies)/2
else
let median=(Amount_of_movies-Amount_of_0_movies)/2+1
fi
If you use the (()) form instead of let, you can uses spaces to make your formulas more readable. You can also use them in if statements:
(( evencheck = ( Amount_of_movies - Amount_of_0_movies ) % 2 ))
if (( evencheck == 0 ))
then
(( median = ( Amount_of_movies - Amount_of_0_movies ) / 2 ))
else
(( median = ( Amount_of_movies - Amount_of_0_movies ) / 2 + 1 ))
fi

Resources