You bump a counter for every line processed, for every host in a list, or for every
retry. In Bash that almost always means integer arithmetic in $(( … )) or
(( … )), or a for (( … )) header that advances the index for you. The
patterns below stay readable; pay extra attention to post- vs pre-increment when
you reuse the expression’s value in another assignment.
Tested all the commands and code from this article on Ubuntu 25.04, kernel 6.14.0-37-generic, Bash 5.2.37.
For comparisons inside loops, keep bash compare numbers nearby.
Bash add 1 to variable (the usual patterns)
Plain arithmetic expansion: var=$((var + 1))
Works everywhere you need a new value; no side effects beyond assignment:
x=5
x=$((x + 1))
echo "$x"Output:
6Arithmetic command: ((var++)) and ((++var))
(( … )) evaluates like the inside of a C expression. var++ returns the old
value then increments; ++var increments then returns the new value.
Standalone (you only care about the final x):
i=0
((i++))
echo "after post-increment alone: $i"Output:
after post-increment alone: 1When you capture the expression, the difference matters:
i=0
j=$((i++))
echo "j=$j i=$i"
i=0
j=$((++i))
echo "j=$j i=$i"Output:
j=0 i=1
j=1 i=1Compound assignment inside (( ))
x=5
((x += 2))
echo "$x"Output:
7let (older style, still valid)
let x=1
let x+=2
echo "$x"Output:
3expr (external, POSIX—avoid for hot loops)
y=1
y=$(expr "$y" + 1)
echo "$y"Output:
2Bash increment variable in loop
C-style for (( … ))
The third clause of for ((i=0; i<n; i++)) is the classic bash increment
variable in loop form (see the full
bash for loop write-up):
for ((i = 0; i <= 3; i++)); do
echo "i=$i"
doneOutput:
i=0
i=1
i=2
i=3Separate counter over a word list
When you iterate words but still need an index:
n=0
for name in a b c; do
echo "$n: $name"
((n++))
done
echo "final count: $n"Output:
0: a
1: b
2: c
final count: 3Bash increment counter with while
Same arithmetic; often paired with read or a condition (see
bash while loop):
count=0
while [[ $count -lt 3 ]]; do
echo "count=$count"
((count++))
doneOutput:
count=0
count=1
count=2Integer-only arithmetic
(( … )) and $(( … )) treat variables as integers. Strings that are not valid
integers cause errors—normalize input first or stay in string land.
Declare integers when it helps clarity (optional):
declare -i hits=0
((hits++))
echo "$hits"Output:
1Pitfalls worth remembering
set -u: incrementing an unset variable is an error—initializen=0before((n++)).set -e:((x++))returns exit status 1 when the expression’s value is 0 (post-increment still “returns” the old value). Starting fromx=0, a bare((x++))can therefore stop the script. Safer underset -e:((++x)),((x+=1)), orx=$((x+1)).- Post vs pre inside assignments (
$((i++))vs$((++i)))—easy to get off-by-one when you also use the return value. - Floating point: use
bcorawk;((x += 0.1))is not the Bash integer model.
Quick reference (pick one style per script)
| Style | Example | Typical use |
|---|---|---|
| Expansion assign | x=$((x+1)) |
clearest “add n” |
(( post |
((x++)) |
bump inside loops |
(( pre |
((++x)) |
need new value in same expression |
(( add n |
((x+=5)) |
step sizes other than 1 |
let |
let x+=1 |
legacy scripts |
expr |
x=$(expr "$x" + 1) |
portability without Bash |
Summary
Incrementing in Bash is integer work: x=$((x+1)) is the clearest “add one,” ((x++))
or ((++x)) match C habits (watch post- vs pre-increment when you reuse the
expression’s value), and the third clause of for ((…; …; i++)) is the usual loop
counter form. while and until counters follow the same arithmetic rules. Stick to
one style per script, initialize variables before set -u, and reserve expr for
rare portability needs—(( )) keeps counter code short and fast.
Further reading:

