| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | bash 5.2.26-6.el10gawk 5.3.0-8.el10bc 1.08.2-5.el10 |
| Applies to | Linux distributions with Bash installed |
| Privilege | Normal user; no root required for the examples |
| Scope | Integer arithmetic with $(( )) and (( )), operators, increment and decrement, for (( )) loops, number bases, division and remainder, declare -i, legacy let and expr, and decimal work with awk or bc. Does not cover full numeric comparison syntax in [[ ]] or advanced floating-point libraries. |
| Related guides | Bash shell script tutorial Shell scripting course hub |
Bash provides arithmetic contexts for evaluating integer expressions directly in shell scripts. Use $(( )) when you need the calculated value in an assignment or command line. Use (( )) when you want to test an arithmetic expression or update variables directly.
Bash handles integer arithmetic natively. For decimal calculations, awk and bc are two common choices. The main difference is whether you need the calculated value from $(( )) or need (( )) to run as a command with an exit status.
Why result=$a+$b Does Not Perform Arithmetic
Normal shell assignment does not calculate. It stores the text on the right of =:
a=5
b=3
result=$a+$b
printf '%s\n' "$result"5+3Bash expanded the variables but treated + as ordinary text, so the assigned value became 5+3. To add the numbers, use arithmetic expansion:
result=$((a + b))
printf '%s\n' "$result"8$(( expression )) is Bash arithmetic expansion: it evaluates an integer expression and substitutes the result.
Bash Arithmetic in One Table
| Form | What it does | Typical use |
|---|---|---|
$(( expression )) |
Arithmetic expansion — substitutes the numeric result | total=$((a + b)), printf '%s\n' "$((n ** 2))" |
(( expression )) |
Arithmetic command — evaluates expressions, can update variables, and returns status 0 for nonzero or 1 for zero |
(( count < max )), ((i++)) |
declare -i name |
Integer attribute on assignment | declare -i total — values assigned are evaluated as arithmetic |
let expression |
Legacy arithmetic builtin using the same arithmetic expression syntax | let "x += 3" — prefer (( )) in new scripts |
expr 5 + 3 |
Older external arithmetic syntax | Recognize in legacy scripts; prefer Bash arithmetic in new code |
$(( )) returns a value. (( )) is a command: it exits 0 when the expression is nonzero and 1 when the expression evaluates to zero.
Arithmetic Expansion with $(( ))
Use $(( )) when you need the calculated value in an assignment, command argument, or output.
Add two variables:
a=5
b=3
printf 'sum=%s\n' "$((a + b))"sum=8Spaces inside $(( )) are optional; a+b and a + b both work. Variable names do not need a leading $ inside the parentheses. Bash also evaluates the value of a referenced variable as an arithmetic expression. Keep this in mind when arithmetic operands come from external input.
Assign the result back to a variable:
count=0
count=$((count + 1))
printf 'count=%s\n' "$count"count=1Nested parentheses group sub-expressions: $(( (a + b) * 2 )).
Arithmetic Command (( ))
(( expression )) evaluates an arithmetic expression without expanding the result into the command line. The expression can modify variables, and its final numeric value determines the exit status: nonzero succeeds with status 0, zero fails with status 1.
Check the exit status when the expression evaluates to zero:
bash -c '(( 0 )); printf "zero exit=%s\n" "$?"'zero exit=1Use (( )) in if, while, and until when you want a numeric test without $(( )) expansion; the Bash if, elif, else guide covers command-status branching in the same structures:
size_kb=250
limit_kb=100
if (( size_kb > limit_kb )); then
printf 'over limit\n'
fiover limitWhen (( expression )) is used as a standalone command in a context where set -e applies, a zero result can terminate the script. That matters for postfix increment — covered in the increment section below.
Bash Arithmetic Operators
Common Bash arithmetic operators include:
| Type | Operators |
|---|---|
| Basic math | + - * / % ** |
| Increment/decrement | ++ -- |
| Comparison | < <= > >= == != |
| Logical | ! && || |
| Bitwise | ~ & | ^ << >> |
| Assignment | = += -= *= /= %= <<= >>= &= ^= |= |
| Conditional | condition ? true_value : false_value |
Use == and != for equality tests. = is assignment — (( a = b )) stores b into a, it does not compare them.
Raise a value to a power with **:
printf '%s\n' "$((2 ** 3))"8Combine assignment and addition inside (( )):
x=2
((x += 3))
printf 'x=%s\n' "$x"x=5Comparisons evaluate to 1 for true and 0 for false:
a=10
b=20
printf 'a<b=%s\n' "$((a < b))"a<b=1The ternary operator picks one of two values:
count=12
status=$(( count > 10 ? 1 : 0 ))
printf 'status=%s\n' "$status"status=1For tests outside arithmetic context — for example [[ "$x" -eq 5 ]] — see Bash compare numbers.
Increment, Decrement and Arithmetic Loops
Prefix and postfix forms work inside (( )) and in for (( )) headers: ++var, var++, --var, var--. Variable names in for (( )) do not take a leading $.
A C-style arithmetic loop initializes, compares, and increments in one header. The Bash for loop guide covers list-form loops and other for (( )) patterns:
for ((i = 0; i < 3; i++)); do
printf 'i=%s\n' "$i"
donei=0
i=1
i=2A standalone ((i++)) can unexpectedly terminate a script using set -e when i starts at 0. ((i++)) evaluates to 0 on the first bump, so the arithmetic command fails and the script exits:
bash -c 'set -e; i=0; ((i++)); echo after-i++' 2>&1; printf 'exit=%s\n' "$?"exit=1set -e does not terminate for failures used as tests in if, while, until, and many && / || contexts; see Bash exit codes for when errexit still applies. Prefer i=$((i + 1)) in strict-mode scripts, or see Bash increment variable for post- vs pre-increment patterns.
Number Bases and Leading Zeros
Bash accepts base#number in arithmetic, with bases from 2 through 64. You can also write hexadecimal values with a 0x prefix.
Binary and hexadecimal literals:
printf '%s\n' "$((2#1010))"10Hexadecimal literals use base 16:
printf '%s\n' "$((16#FF))"255A leading 0 without base# selects octal. That is why bare 08 fails — 8 is not a valid octal digit:
bash -c 'printf "%s\n" $((08))' 2>&1bash: line 1: 08: value too great for base (error token is "08")Force base 10 with the 10# prefix:
printf '%s\n' "$((10#08))"8Parse a zero-padded ID from a variable the same way:
build_id=00042
seq=$((10#$build_id))
printf 'seq=%s\n' "$seq"seq=42Without 10#, a value such as 010 is octal and evaluates to 8:
declare -i n=010
printf 'n=%s\n' "$n"n=8If a value is known to contain decimal digits but may be zero-padded, use 10# to force base 10.
Integer Division, Modulo and Division by Zero
Bash arithmetic uses signed integers. Division truncates toward zero; % returns the remainder:
printf 'div=%s rem=%s\n' "$((17 / 5))" "$((17 % 5))"div=3 rem=2The % operator returns the remainder, which is useful for checks such as whether a number is even: (( n % 2 == 0 )).
Bash uses fixed-width integers and does not check for arithmetic overflow, so sufficiently large values can wrap.
Division by zero is a hard error:
bash -c 'printf "%s\n" $((5 / 0))' 2>&1bash: line 1: 5/0: division by 0 (error token is "0")Guard divisors before you divide.
Decimal and Floating-Point Math with awk and bc
Bash has no built-in floating-point arithmetic. Use awk for convenient formatted calculations or bc when you need control over calculation scale.
Compute a percentage with awk — printf in awk rounds the displayed value:
awk 'BEGIN{printf "pct=%.1f\n", 3/8*100}'pct=37.5Pipe an expression to bc:
printf 'scale=2; 3/8\n' | bc.37For division, scale=2 calculates two digits after the decimal point without rounding, so 3/8 becomes .37, not .38. For rounded decimal output, awk with printf "%.2f" is usually clearer.
Avoid starting a new awk or bc process on every iteration of a large loop when the calculations can be batched into one call.
declare -i, let and Legacy expr
declare -i gives a variable the integer attribute, so values assigned to it are evaluated as arithmetic expressions:
declare -i total
total="2 + 3"
printf 'total=%s\n' "$total"total=5Unlike a normal assignment, assigning to a declare -i variable performs arithmetic evaluation immediately. See Bash variables for other declare attributes.
The legacy let builtin accepts the same expressions as (( )):
x=2
let "x += 3"
printf 'x=%s\n' "$x"x=5Older scripts sometimes call expr as an external command:
expr 5 + 38Recognize expr when reading legacy code; prefer $(( )) or (( )) in new scripts.
Common Bash Arithmetic Errors
| Symptom | Likely cause | Fix |
|---|---|---|
5+3 instead of 8 |
Normal assignment, not arithmetic | Use $((a + b)) |
value too great for base on 08 or 09 |
Leading zero implies octal | Use $((10#08)) or strip leading zeros |
Script exits under set -e on ((i++)) |
Postfix increment returned 0 as standalone command |
Use i=$((i + 1)) |
division by 0 |
Divisor was zero | Test the divisor before / or % |
3 / 8 prints 0 |
Integer division truncates | Use awk or bc for decimal math |
Missing variable behaves like 0 |
Without set -u, unset or null variables in arithmetic normally evaluate as 0; with set -u, an unset variable is an error |
Initialize variables; account for set -u |
bc shows .37 not .38 |
scale truncates; does not round |
Use awk printf for rounded display |
Bash Percentage and Threshold Example
This example uses Bash arithmetic for the integer threshold comparison and awk to calculate the percentage. With a known nonzero limit, compare two integer counts:
used_kb=85
limit_kb=100
if (( used_kb > limit_kb )); then
printf 'over limit: %s/%s KB\n' "$used_kb" "$limit_kb"
else
pct=$(awk -v u="$used_kb" -v l="$limit_kb" 'BEGIN{printf "%.0f", u/l*100}')
printf 'within limit: %s%% (%s/%s KB)\n' "$pct" "$used_kb" "$limit_kb"
fiwithin limit: 85% (85/100 KB)Summary
Bash evaluates integer expressions with $(( )) when you need the result and (( )) when you need an arithmetic command for tests or updates. Normal assignment does not calculate; use $((a + b)) rather than $a+$b.
Bash arithmetic is integer-only. Division truncates fractional results, and leading zeros can trigger octal interpretation. Use awk or bc when you need decimal calculations.
References
- GNU Bash manual — Shell Arithmetic — operators, bases, and expansion rules
- GNU Bash manual — Arithmetic Expansion —
$(( ))syntax - GNU Bash manual — Conditional Constructs —
(( expression ))return status - GNU Bash manual — The Set Builtin —
set -eand its exceptions - GNU Awk manual —
BEGINblocks and formatted output - GNU bc manual —
scaleand decimal division
Frequently Asked Questions
What is the difference between (( )) and $(( ))?
$(( expression )) evaluates the expression and substitutes the result, so you can assign it or print it. (( expression )) runs as a command: it can modify variables, and its final value sets exit status (0 when nonzero, 1 when zero). Use $(( )) for values and (( )) for tests and direct updates.
Does Bash support floating point?
Not in native shell arithmetic. $(( )) and (( )) work on integers only. Use awk for formatted decimal calculations or bc when you need controlled calculation scale.
Why does 08 fail in Bash arithmetic?
A leading 0 selects base 8 (octal). 8 and 9 are not valid octal digits, so 08 errors. Use $((10#08)) when the input is decimal text with a leading zero.
Why can ((i++)) stop a strict-mode script?
Postfix ((i++)) evaluates to the old value. When i is 0, that value is 0, so a standalone (( )) command can exit with status 1. With set -e in an applicable context, the script terminates. Use i=$((i + 1)) or read the increment guide for other patterns.

