bash ((count++)) kills your set -e script
I wrote a Terraform import script that loops over ~40 SSM parameters and counts how many it imports successfully. Standard stuff:
set -euo pipefail
imported=0
for key in "${KEYS[@]}"; do
terraform import "aws_ssm_parameter.tenant_config[\"${key}\"]" "/path/${key}"
((imported++))
done
echo "Imported: ${imported}"
The first import succeeded. Then the script exited silently. No error, no stack trace, no “Imported: 1.” Just gone.
What bash thinks ((0)) means
Bash arithmetic expressions don’t return whether the operation succeeded. They return whether the result is truthy:
$ ((1)); echo $?
0
$ ((0)); echo $?
1
((0)) evaluates to zero, which bash considers false, which means exit code 1. And set -e treats any non-zero exit code as fatal.
Now look at ((imported++)). The ++ is post-increment — it returns the value before incrementing. When imported is 0:
- Post-increment returns 0 (the old value)
importedbecomes 1- Bash sees the return value 0, which is falsy
- Exit code 1
set -ekills the script
The counter incremented correctly. Bash killed the script anyway.
Three ways to not die
# Suppress the exit code
((imported++)) || true
# Assignment always succeeds regardless of value
imported=$((imported + 1))
# Pre-increment returns the new value, so first call returns 1, not 0
((++imported))
|| true for patching existing code, $((... + 1)) for new code. Pre-increment works but breaks if you ever start from -1.
The seam
(( )) lives between two worlds. In bash, exit code 0 means success. In arithmetic, 0 means false. The (( )) construct maps arithmetic truthiness onto exit codes — zero result becomes non-zero exit code. It’s internally consistent, but it means every (( )) expression that could evaluate to zero is a landmine under set -e.
If you use set -e, audit every (( )) in your scripts. Any expression that can evaluate to zero is a silent exit 1.