Step 1 of 5 · Reading · ~3 min
Learn
Getting Started
Arithmetic
By the end of this lesson you can do integer math in bash, split a quantity into whole units and a remainder with / and %, and avoid the traps that come from bash treating everything as text.
Variables hold text, so 2 + 2 is three words, not math. Arithmetic only happens inside a dedicated context. The one you will use most is the arithmetic expansion $(( )):
apples=7
crates=3
echo "$((apples * crates))" # 21
echo "$(( (apples + 1) * crates ))" # 24
Everything between the double parentheses is evaluated as integer math, and the result is substituted into the line as text. Inside, variables need no $, spaces are free, and the operators are the ones from C: + - * / %, ** for powers, comparisons that give 1 or 0, and the usual precedence (* and / before + and -). When in doubt, add parentheses.
Integer only
echo "$((7 / 2))" # 3 the fraction is dropped
echo "$((-7 / 2))" # -3 truncated toward zero, not floored
echo "$((-7 % 3))" # -1 the remainder takes the sign of the left side
echo "$((3.5 + 1))" # error: bash has no floating point at all
When a script needs decimals, it delegates to a program, which is what bash is for: echo "3.5 + 1.25" | bc -l prints 4.75.
Splitting a quantity with / and %
Division and remainder are a pair. For positive n and d, n / d is how many whole ds fit in n, and n % d is what is left over. Together they split one number into units:
cents=1234
dollars=$((cents / 100)) # 12
left=$((cents % 100)) # 34
quarters=$((left / 25)) # 1
pennies=$((left % 25)) # 9
echo "$dollars dollars, $quarters quarters, $pennies pennies"
Each step works on the remainder of the step before. A useful check is that the pieces add back up: 100 * 12 + 34 is 1234.
Statement forms
count=$((count + 1)) # assignment through an expansion
((count++)) # the same thing as a command
(( total += 5 )) # compound assignment, only inside (( ))
Outside an arithmetic context total+=5 is string append: with total=10 it makes total the text 105. That is a quiet and nasty bug, so do your math inside (( )) or $(( )).
Where numbers come from
In real scripts numbers arrive as text from read or from other programs. Inside $(( )) text that looks numeric is converted automatically:
read -r n # n holds the text 42
echo "$((n * 2))" # 84
One sharp edge: a number with a leading zero is octal. $((010)) is 8, and $((08)) is an error because 8 is not an octal digit. Force decimal with 10#: $((10#$n)). Non-numeric text in an arithmetic context is treated as a variable name, which usually means 0, silently.
You will also see expr 7 + 2 in old scripts. Recognise it, and never write it.
Common mistakes
- No expansion.
echo "n * 2"prints the charactersn * 2. Without$(( ))there is no math. - Bare
$((...))as a command.$((7 * 3))on a line by itself makes bash try to run a program named21. - Expecting decimals.
$((7 / 2))is 3, never 3.5. - Reading remainders wrongly. The remainder is always smaller than the divisor, so a minutes value of 75 means a division is missing.
Your exercise
Convert a number of seconds into hours, minutes and seconds. Each unit is carved out of what the larger unit left behind, as in the coin example, but with three levels. The graded edge cases are zeros in the middle (3600) and values just below a boundary (3599).
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…