4. Arithmetic evaluation and expansion
We have seen before $((expression))
where expression is an
arithmetic expression. It is related to the compound command ((...))
which is used for arithmetic evaluation (truth tests). Here we will
see some more arithmetic operators and expressions.
-
By default numbers are treated as decimals (base 10). But we can also use octal numbers (base 8), hexadecimal numbers (base 16), etc.
echo $((99)) # decimal
echo $((077)) # octal
echo $((0xff)) # hexadecimal
echo $((2#11)) # binary
echo $((7#66)) # base 7
-
Arithmetic operators:
echo $((5 + 2))
echo $((5 - 2))
echo $((5 * 2))
echo $((5 ** 2))
echo $((5 / 2))
echo $((5 % 2))
An example:
vim modulo.sh
modulo.sh
#!/bin/bash
# modulo: demonstrate the modulo operator
for ((i = 0; i <= 20; i = i + 1)); do
remainder=$((i % 5))
if (( remainder == 0 )); then
printf "<%d> " "$i"
else
printf "%d " "$i"
fi
done
printf "\n"./modulo.sh
-
Assignment:
foo=
echo $foo
if (( foo = 5 )); then echo "It is true."; fi
echo $foo
The
=
sign above makes an assignment, and this assignment is successful. To check for equality we can use==
.Other assignment operators are:
+=
,-=
,*=
,/=
,%=
.There are also incremental/decremental operators:
++
and--
foo=1
echo $((foo++))
echo $foofoo=1
echo $((++foo))
echo $fooLet's see a modified version of the
modulo.sh
example:vim modulo2.sh
modulo2.sh
#!/bin/bash
# modulo: demonstrate the modulo operator
for ((i = 0; i <= 20; ++i)); do
if ((i % 5 == 0)); then
printf "<%d> " "$i"
else
printf "%d " "$i"
fi
done
printf "\n"diff -u modulo.sh modulo2.sh
diff -u modulo.sh modulo2.sh
--- modulo.sh 2023-06-28 01:48:25.000000000 +0000
+++ modulo2.sh 2023-06-28 01:48:25.000000000 +0000
@@ -2,9 +2,8 @@
# modulo: demonstrate the modulo operator
-for ((i = 0; i <= 20; i = i + 1)); do
- remainder=$((i % 5))
- if (( remainder == 0 )); then
+for ((i = 0; i <= 20; ++i)); do
+ if ((i % 5 == 0)); then
printf "<%d> " "$i"
else
printf "%d " "$i"./modulo2.sh
-
There are also some operators that work at the bit level:
~
-- Negate all the bits in a number.<<
-- Shift all the bits in a number to the left.>>
-- Shift all the bits in a number to the right.&
-- Perform an AND operation on all the bits in two numbers.|
-- Perform an OR operation on all the bits in two numbers.^
-- Perform an exclusive OR operation on all the bits in two numbers.
There are also corresponding assignment operators (for example,
<<=
) for all but bitwise negation.Let's see an example that prints the powers of 2:
for ((i=0;i<8;++i)); do echo $((1<<i)); done
-
The compound command
((...))
supports also comparison operators:==
,!=
,<
,<=
,>
,>=
,&&
(logical AND),||
(logical OR).It also supports the ternary operator:
expr1?expr2:expr3
. If expressionexpr1
evaluates to be non-zero (arithmetic true), thenexpr2
; elseexpr3
.Logical expressions follow the rules of arithmetic logic; that is, expressions that evaluate as zero are considered false, while non-zero expressions are considered true. The
((...))
compound command maps the results into the shell’s normal exit codes.if ((1)); then echo "true"; else echo "false"; fi
if ((0)); then echo "true"; else echo "false"; fi
The ternary operator is like a compact
if/then/else
statement:a=0
((a<1?++a:--a))
echo $a
((a<1?++a:--a))
echo $a
a=$((a<1?a+1:a-1))
echo $a
-
Let's see a more complete example of using arithmetic operators in a script that produces a simple table of numbers.
vim arith-loop.sh
arith-loop.sh
#!/bin/bash
# arith-loop: script to demonstrate arithmetic operators
finished=0
a=0
printf "a\ta**2\ta**3\n"
printf "=\t====\t====\n"
until ((finished)); do
b=$((a**2))
c=$((a**3))
printf "%d\t%d\t%d\n" "$a" "$b" "$c"
((a<10?++a:(finished=1)))
done./arith-loop.sh
-
For complex arithmetics we can use
bc
, which is an arbitrary precision calculator.bc <<< '2 + 2'
echo '2 + 2' | bc
This example script calculates monthly loan payments:
vim loan-calc.sh
loan-calc.sh
#!/bin/bash
# loan-calc: script to calculate monthly loan payments
PROGNAME="${0##*/}" # Use parameter expansion to get basename
usage () {
cat <<- EOF
Usage: $PROGNAME PRINCIPAL INTEREST MONTHS
Where:
PRINCIPAL is the amount of the loan.
INTEREST is the APR as a number (7% = 0.07).
MONTHS is the length of the loan's term.
EOF
}
if (($# != 3)); then
usage
exit 1
fi
principal=$1
interest=$2
months=$3
bc <<- EOF
scale = 10
i = $interest / 12
p = $principal
n = $months
a = p * ((i * ((1 + i) ^ n)) / (((1 + i) ^ n) - 1))
print a, "\n"
EOF./loan-calc.sh 135000 0.0775 180
This example calculates the monthly payment for a $135,000 loan at 7.75 percent APR for 180 months (15 years). Notice the precision of the answer. This is determined by the value given to the special
scale
variable in thebc
script.For more details about
bc
see:man bc
info bc