Bash compare numbers and integers: variables, [[ ]], (( )), and pitfalls

Tech reviewed: Deepak Prasad
Bash compare numbers and integers: variables, [[ ]], (( )), and pitfalls

Most scripts eventually need a bash numeric comparison: bash compare variable to number, bash greater than / less than between two counters, or a quick bash integer comparison before doing work. Bash only does integer math in [[ / [ / (( )) the way people expect; decimals need something like bc. The patterns below are the ones I reach for daily, plus a few footguns (leading zeros, empty variables, using < as a redirect inside [).

Tested all the commands and code from this article on Ubuntu 25.04, kernel 6.14.0-37-generic, Bash 5.2.37.


Integer operators (compare numbers in Bash)

These operators expect integers (optionally with a leading -). They work inside [ ... ], [[ ... ]], and test ....

Operator Meaning Example
-eq equal [[ $a -eq $b ]]
-ne not equal [[ $a -ne $b ]]
-lt less than [[ $a -lt $b ]]
-le less or equal [[ $a -le $b ]]
-gt greater than [[ $a -gt $b ]]
-ge greater or equal [[ $a -ge $b ]]

Bash compare variable to number with [[ or [

Quote variables so empty values do not break the parser. Comparing a variable to a literal is the same as comparing two variables:

bash
count=3

if [[ "$count" -eq 3 ]]; then
  echo "count is exactly three"
fi

[[ is a Bash keyword: you can combine tests with && / || without extra brackets, and you avoid some classic [ footguns. [ / test is still what you want when the shebang is plain sh on a minimal system.

bash
a=5
b=10

if [ "$a" -lt "$b" ]; then
  echo "a is less than b"
fi

Output from that snippet:

text
a is less than b

Same check with [[:

bash
a=5
b=10

if [[ $a -lt $b ]]; then
  echo "a is less than b"
fi

Same line on stdout:

text
a is less than b

Bash if numeric comparison with (( ))

Inside (( ... )), Bash treats the interior as arithmetic. You can use C-style operators (<, <=, ==, !=, >, >=) and skip $ on simple variable names:

bash
a=5
b=10

if (( a < b )); then
  echo "a is less than b"
fi

That prints:

text
a is less than b

(( )) exits 1 when the expression evaluates to zero, which is handy for while (( i < n )) style loops. For “compare numbers in bash” when the math gets longer, (( )) tends to read cleaner than a chain of -lt / -gt.


The test builtin

test and [ are the same family. This is portable and boring in a good way:

bash
a=5
b=10

if test "$a" -lt "$b"; then
  echo "a is less than b"
fi

Output:

text
a is less than b

Branching and loops

For branching, pair numeric tests with normal if / else flow. Timing or logging comparisons in real scripts often sits next to ideas from get script execution time.

bash
a=20
b=10

if [ "$a" -gt "$b" ]; then
  echo "a is greater than b"
else
  echo "a is not greater than b"
fi

What I get:

text
a is greater than b

while and until

Bash compare integers in a loop by re-testing the condition each iteration:

bash
a=1

while [ "$a" -lt 5 ]; do
  echo "Number is $a"
  ((a++))
done

Output:

text
Number is 1
Number is 2
Number is 3
Number is 4

until flips the sense (run while the test is false):

bash
a=0

until [ "$a" -ge 5 ]; do
  echo "Number is $a"
  ((a++))
done

Output:

text
Number is 0
Number is 1
Number is 2
Number is 3
Number is 4

More loop shapes live in the dedicated while write-up if you need them.


Pitfalls worth memorizing

Leading zeros and “value too great for base”

Arithmetic treats numbers with a leading 0 as octal. 08 is invalid octal, so both [[ and (( )) can error:

bash
a=08
b=8
[[ $a -eq $b ]] 2>&1 || true

Typical stderr:

text
bash: [[: 08: value too great for base (error token is "08")

Strip leading zeros, read fields with 10# in arithmetic ($((10#$a))), or avoid padding unless you control the format.

Do not use < or > inside [ for integers

Inside [, < and > are redirection, not “less than”. Even in [[, use -lt / -gt for integer relations; < / > there compare strings in collation order, which is not the same as numeric order for multi-digit values.

Numeric check:

bash
[[ 9 -lt 10 ]] && echo "numeric nine is less than ten"

Lexicographic pitfall (string rules, not counting):

bash
[[ 9 < 10 ]] && echo "would be wrong for integers" || echo "string compare did not say 9 < 10"

On my shell that prints:

text
numeric nine is less than ten
string compare did not say 9 < 10

Empty or unset variables

[[ "$n" -eq 5 ]] is safe when n is empty (it is treated as 0 for the numeric context in recent Bash, but the clearer habit is [[ -n "$n" ]] && [[ "$n" -eq 5 ]] or default the value: n=${n:-0}).

Floating point

Bash arithmetic is integer-only. For decimals, call bc, awk, or Python — do not pretend 3.14 works inside -eq.


Summary

Bash numeric comparison comes down to picking an integer form: [[ "$a" -gt "$b" ]] and friends for clarity and combining conditions, (( a > b )) when the expression already looks like arithmetic, and test / [ when you care about POSIX sh. Comparing a variable to a literal uses the same operators—keep operands quoted, use -eq / -lt / -gt instead of < / > for real integer relations, watch leading zeros in values like 08, and use bc or awk when you need decimals. With that, if branches and loops over integers stop being guesswork.

Further reading:

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with over a decade of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, …

  • Red Hat Certified System Administrator in Red Hat OpenStack
  • Certified Kubernetes Application Developer (CKAD)
  • Red Hat Certified Specialist in Ansible Automation
  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security