Theory
Why n=n+1 does not add
In the shell, values are text by default. So if you write n=5 and then n=n+1, you do not get 6, you get the literal string 'n+1'. The shell did not do any arithmetic, because you did not ask it to.
To actually compute, you must use one of the shell's arithmetic tools. There are two: the older expr command, and the modern $(( )) form. This lesson covers both, and the one limitation that surprises everyone: shell arithmetic is whole numbers only.
Theory
Two ways to compute
expr is an older, external command. You write expr 7 + 3, and it prints 10. It needs spaces around each part, and because * is a wildcard, you must escape multiplication as \*, which is fiddly.
$(( )) is the modern arithmetic expansion built into the shell, and the preferred choice. You write $(( 7 + 3 )) and it becomes 10, no escaping needed, and it reads cleanly. It also powers the common increment idiom you have already seen in loops: i=$((i+1)). Prefer $(( )) in new scripts; recognise expr when you meet it in older ones.
Practical
Arithmetic both ways (verified)
$ expr 7 + 3 # older command
10
$ echo $(( 7 + 3 )) # modern, preferred
10
$ echo $(( 7 % 2 )) # modulo (remainder)
1
# The increment idiom used in loops:
$ i=1
$ i=$(( i + 1 ))
$ echo $i
2Watch out
Integer only: 7 / 2 is 3, not 3.5
Shell arithmetic works in whole numbers only. So $(( 7 / 2 )) is 3, not 3.5, the fractional part is simply thrown away (truncated). There are no decimals at all in expr or $(( )).
If you genuinely need fractions (say, an average or a percentage), the shell cannot do it alone; you use a separate tool such as bc (echo "scale=2; 7/2" | bc gives 3.50) or awk. Forgetting this and expecting 3.5 from $(( 7 / 2 )) is one of the most common shell-scripting surprises.
Quiz
In a shell script, what does echo $(( 7 / 2 )) print?
- 3.5, the exact quotient
- 3, because shell arithmetic is integer only and truncates the fractional part
- 4, rounding up
- An error, because division is not allowed
Show the answer
3, because shell arithmetic is integer only and truncates the fractional part
Shell arithmetic works only in whole numbers, so $(( 7 / 2 )) computes the integer quotient and discards the remainder, giving 3 (not 3.5). Option A assumes decimals, which the shell's $(( )) and expr simply do not support. Option C is wrong: it truncates (drops the fraction), it does not round, so 7/2 is 3, and even 9/2 would be 4, not 5. Option D is wrong: division is perfectly allowed; it just yields an integer result. If you need the real value 3.5, you must use a floating-point tool like bc. The takeaway: expect whole-number results from shell maths, and reach for bc when you need fractions.
Think first
Why is shell arithmetic integer-only in the first place?
Most languages handle decimals easily. Why does the shell restrict its built-in maths to whole numbers? Then tap.
Show the answer
Because the shell's job is orchestrating commands and files, not heavy number-crunching, so its built-in arithmetic is kept small and simple, covering the whole-number tasks scripts actually need. Almost all arithmetic in shell scripts is counting and indexing: incrementing a loop counter, counting files, computing a line number, checking how many attempts remain, and those are naturally whole numbers. Supporting full floating-point maths inside the shell would add complexity and edge cases (precision, rounding, formatting) for a need that rarely arises in the shell's core role. So the designers kept it integer-only and left serious or fractional maths to specialised tools that already do it well, bc for arbitrary-precision decimals, awk for calculations over data, Python for anything richer. This reflects the same Unix philosophy you keep meeting: each tool does one thing well and hands off the rest. The shell counts and coordinates; when you need real numbers, you pipe to a calculator built for them. Knowing this, you will not fight the shell for decimals, you will reach for bc without hesitation, and your scripts stay clean.
Summary
Key takeaways
- The shell treats values as text by default, so n=n+1 does not add; you must ask for arithmetic explicitly.
- expr is the older external command: expr 7 + 3 prints 10 (spaces required, * must be escaped).
- $(( )) is the modern, preferred arithmetic expansion: $(( 7 + 3 )) is 10, cleaner and built in.
- The loop increment idiom i=$(( i + 1 )) uses this; % gives the remainder ($(( 7 % 2 )) is 1).
- Shell arithmetic is integer only: $(( 7 / 2 )) is 3, not 3.5, the fraction is truncated.
- For real (fractional) numbers, use a separate tool like bc or awk.
- Memory hook: $(( )) for whole-number maths; bc when you need decimals.