Bash Error Guide: 'integer expression expected' — Fix Numeric Test Comparisons
Fix 'integer expression expected' in Bash: handle empty or non-numeric variables in -eq/-lt tests, quote and default values, and use arithmetic tests correctly.
- #bash
- #automation
- #troubleshooting
- #errors
Stuck on this Bash & Python Automation error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
Bash raises this whenever a numeric test comparison operator (-eq, -ne, -lt, -le, -gt, -ge) is handed something that is not an integer. The classic trigger is an empty or non-numeric variable inside [ ... ]:
./check.sh: line 12: [: foo: integer expression expected
The empty-variable variant is even more common and confusing, because the offending value is invisible:
./check.sh: line 12: [: : integer expression expected
That empty space after the colon is the value — a variable that expanded to nothing. The word before “integer expression expected” is exactly what Bash tried and failed to read as a number.
Symptoms
- A script that works with normal input fails when a variable is empty, unset, or contains text.
- The error names a value that clearly isn’t a number (
foo,12.5,10G,3). - The error shows an empty value (
[: : integer expression expected) — a variable expanded to nothing. - Comparing counts, exit codes, sizes, or parsed command output intermittently breaks depending on the data.
- Floating-point or human-readable numbers (
3.14,2K,1,000) always fail becausetestonly understands base-10 integers.
Common Root Causes
- Empty or unset variable — the variable was never set, a command produced no output, or a typo left it undefined; with
[it expands to nothing and fails the numeric test. - Non-numeric content — the variable holds text, a float, a unit suffix (
10G), a comma-grouped number, or has leading/trailing whitespace or newlines. - Unquoted command substitution with extra whitespace —
$(wc -l < file)on some systems yields leading spaces or a trailing newline that breaks the comparison. - Comparing strings with numeric operators — using
-eqto compare strings (should be=), or=/<to compare numbers (should be-eq/-lt). - Locale or formatting — numbers formatted with thousands separators or a decimal comma.
- Word-splitting surprises — an unquoted variable containing multiple words turns
[ $x -gt 5 ]into a malformed test with too many arguments or the wrong token in the numeric slot.
Diagnostic Workflow
Reproduce and inspect the exact value, making whitespace visible:
printf 'value=[%s]\n' "$count" # brackets reveal empty strings and stray spaces
printf 'bytes=[%q]\n' "$count" # %q shows tabs/newlines/quoting explicitly
Trace which line and which value the shell chokes on:
bash -x ./check.sh 2>&1 | grep -n 'integer expression' # -x prints each test with expanded values
Test whether the value is actually a clean integer:
[[ "$count" =~ ^-?[0-9]+$ ]] && echo "integer" || echo "NOT an integer: [$count]"
Check for the common command-substitution whitespace trap:
lines=$(wc -l < /etc/hosts)
printf 'lines=[%s]\n' "$lines" # some wc builds pad with leading spaces
If the source is a float or has a suffix, confirm what produced it:
echo "$count" | od -c | head # reveals hidden characters, newlines, and CR (\r) from Windows files
Example Root Cause Analysis
A deploy gate counted ready pods and compared against a threshold:
ready=$(kubectl get pods -l app=web --field-selector=status.phase=Running --no-headers | wc -l)
if [ "$ready" -ge 3 ]; then
echo "enough replicas"
fi
Intermittently it failed with:
deploy-gate.sh: line 3: [: : integer expression expected
The clue was the empty value. When the label selector matched no pods, kubectl printed nothing to stdout but also emitted No resources found to stderr — so wc -l counted zero lines and ready became 0… except on the runs that failed, an authentication hiccup made kubectl exit non-zero and print nothing at all, leaving ready empty. [ "" -ge 3 ] is exactly the empty-variable case.
The fix defends the comparison against non-integer input rather than trusting upstream output:
ready=$(kubectl get pods -l app=web --field-selector=status.phase=Running --no-headers 2>/dev/null | wc -l)
ready=${ready//[^0-9]/} # strip any non-digits (whitespace, stray chars)
ready=${ready:-0} # default to 0 if empty
if [[ "$ready" =~ ^[0-9]+$ ]] && [ "$ready" -ge 3 ]; then
echo "enough replicas: $ready"
else
echo "replica check failed or too few ready: [$ready]" >&2
exit 1
fi
Now an empty or garbage value is normalized to 0 and validated before the numeric comparison ever runs, so the gate fails clean instead of erroring.
Prevention Best Practices
- Default every numeric variable — use
${var:-0}so an empty value becomes a valid integer instead of blowing up the test. - Validate before comparing — guard numeric tests with
[[ "$var" =~ ^-?[0-9]+$ ]]so non-numeric input is rejected with a clear message. - Always quote —
[ "$x" -gt 5 ], never[ $x -gt 5 ]; quoting prevents word-splitting and turns an empty value into a single empty argument rather than a syntax error. - Use
[[ ]]and(( ))— Bash’s[[ ]]is more forgiving, and arithmetic context(( x >= 3 ))treats an empty/undefined value as0(withset -uoff) and reads numbers naturally. - Strip whitespace from command output —
var=${var//[^0-9]/}orread-trim before comparing counts fromwc,grep -c, etc. - Handle floats separately —
testis integer-only; useawk 'BEGIN{exit !('"$a"' > '"$b"')}'orbc -lfor floating-point comparisons. - Watch for CRLF — files edited on Windows add
\r; rundos2unixor strip\rso3\risn’t treated as non-numeric.
Quick Command Reference
printf '[%s]\n' "$var" # reveal empty strings / stray whitespace
printf '[%q]\n' "$var" # reveal tabs, newlines, CR
[[ "$var" =~ ^-?[0-9]+$ ]] # is it a clean integer?
var=${var:-0} # default empty/unset to 0
var=${var//[^0-9]/} # strip non-digit characters
(( var >= 3 )) && echo ok # arithmetic comparison
bash -x ./script.sh # trace expanded test values
echo "$var" | od -c # find hidden characters / CR
Conclusion
integer expression expected means a numeric test operator received something that isn’t a base-10 integer — most often an empty variable, stray whitespace, a \r from a Windows file, or a value that was always text or a float. The fix is defensive input handling: quote the variable, default it with ${var:-0}, strip non-digits, and validate with a regex before comparing. Reach for (( )) arithmetic context for numbers and [[ ]] string tests for text, and reserve -eq/-lt for values you have already proven are integers.
Fixed it? Get 500 Bash & Python Automation & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.