6.2. Comparison & Logic#

Beyond arithmetic, you need to compare values and combine conditions using logic. These operators form the foundation of decision-making in scripts.

6.2.1. Comparison Operator Pitfalls#

6.2.1.1. ❌ Using = for Numbers#

# Wrong: This compares as strings, not numbers!
$ [ "10" = "10" ] && echo "equal"      # Works but as string
$ [ "10" = "2" ] && echo "equal"       # Doesn't work (string compare)

# Correct: Use -eq for numeric comparison
$ [ 10 -eq 2 ] && echo "equal"         # Compares as numbers (false)

6.2.1.2. ❌ Unquoted Variables in [ ]#

# Wrong: Can break if variable has spaces
$ name="John Doe"
$ [ $name = "John Doe" ]               # Error: too many arguments!

# Correct: Always quote variables in [ ]
$ [ "$name" = "John Doe" ] && echo "match"

6.2.1.3. ❌ Forgetting Escapes in [ ]#

# Wrong: < and > need escaping in [ ]
$ [ "a" < "b" ]                        # Redirects to file "b"!

# Correct: Escape with \
$ [ "a" \< "b" ] && echo "less"

# Or use [[ ... ]] which doesn't need escaping
$ [[ "a" < "b" ]] && echo "less"

6.2.1.4. (( … )) (Arithmetic context)#

For numeric comparisons only:

# Arithmetic operators and comparisons
$ (( 5 > 3 )) && echo "5 is greater"

# Variables don't need $ or quotes
$ a=10
$ (( a > 5 )) && echo "yes"

# Can combine arithmetic and comparison
$ (( a > 5 && a < 20 )) && echo "between 5 and 20"

# Use for loops and calculations
$ for (( i=0; i<5; i++ )); do
>   echo $i
$ done

Advantages:

  • Clean syntax for numbers

  • Natural comparison operators (>, <, ==, etc.)

  • Good for loops and arithmetic

Disadvantages:

  • Numbers only (no strings)

  • Bash-specific

  • Not useful for file/string tests

6.2.1.5. [[ … ]] (Bash extended test)#

The modern, bash-specific way:

# String comparison with familiar operators
$ [[ "hello" == "hello" ]] && echo "equal"

# Number comparison (still uses -eq, -lt for safety)
$ [[ 5 -eq 5 ]] && echo "equal"

# Logic operators: && (AND), || (OR), ! (NOT)
$ [[ 5 -eq 5 && 3 -lt 10 ]] && echo "both true"

# Variables don't need quotes (safer)
$ name="John Doe"
$ [[ $name == "John Doe" ]] && echo "match"  # No quotes needed!

# Supports pattern matching
$ [[ "test123" == test* ]] && echo "starts with test"

# Supports regex
$ [[ "hello123" =~ [0-9]+ ]] && echo "contains numbers"

Advantages:

  • More intuitive operators (&&, ||, !)

  • Doesn’t require quoting variables

  • Pattern matching and regex support

  • More readable

Disadvantages:

  • Bash-specific (not POSIX)

  • Won’t work in dash or sh

6.2.1.6. [ … ] (POSIX test command)#

The traditional, portable way:

# String comparison
$ [ "hello" = "hello" ] && echo "equal"

# Number comparison (needs -eq, -lt, etc.)
$ [ 5 -eq 5 ] && echo "equal"

# Logic operators: -a (AND), -o (OR), ! (NOT)
$ [ 5 -eq 5 -a 3 -lt 10 ] && echo "both true"

# Must quote variables to avoid word splitting
$ name="John Doe"
$ [ "$name" = "John Doe" ] && echo "match"

Advantages:

  • POSIX standard (works everywhere)

  • Portable to dash, sh, etc.

Disadvantages:

  • Need -eq, -lt for numbers

  • Operators like -a, -o are confusing

  • Quotes required for variables

6.2.2. [ ] vs [[ ]] vs (( ))#

Three ways to test conditions in bash—choose wisely:

6.2.2.1. NOT: ! operator#

Negate a condition (flip true to false, false to true):

# ! inside [ ... ]
$ [ ! 5 -lt 3 ] && echo "5 is NOT less than 3"
5 is NOT less than 3

# ! with [ ]
$ ! [ 5 -lt 3 ] && echo "false negated = true"
false negated = true

# ! with [[ ]]
$ [[ ! "hello" == "world" ]] && echo "not equal"
not equal

# Common: check if variable is NOT empty
$ if [ ! -z "$var" ]; then
>   echo "var has content"
$ fi

NOT logic:

Condition

NOT Condition

true

false

false

true

6.2.2.2. OR: -o (inside [ ]) or || (outside)#

# Inside [ ... ] use -o
$ [ 5 -lt 3 -o 10 -gt 5 ] && echo "at least one true"
at least one true

# Outside [ ... ] use ||
$ [ 5 -lt 3 ] || [ 10 -gt 5 ] && echo "at least one true"
at least one true

# In [[ ... ]] use ||
$ [[ 5 -lt 3 || 10 -gt 5 ]] && echo "at least one true"
at least one true

# Common pattern: default value
$ echo ${var:-"default"}  # Use var, or "default" if var is unset

Truth table for OR:

Condition 1

Condition 2

Result

true

true

true

true

false

true

false

true

true

false

false

false

6.2.2.3. AND: -a (inside [ ]) or && (outside)#

# Inside [ ... ] use -a
$ [ 5 -gt 3 -a 10 -gt 5 ] && echo "both true"
both true

# Outside [ ... ] use &&
$ [ 5 -gt 3 ] && [ 10 -gt 5 ] && echo "both true"
both true

# More readable: use [[ ... ]] with &&
$ [[ 5 -gt 3 && 10 -gt 5 ]] && echo "both true"
both true

# Common pattern: guard clause
$ [ -z "$var" ] && echo "var is empty" || echo "var has value"

Truth table for AND:

Condition 1

Condition 2

Result

true

true

true

true

false

false

false

true

false

false

false

false

6.2.3. Logical Operators: AND, OR, NOT#

Combine conditions to make complex decisions:

6.2.3.1. The =, ==, !=, <, > Operators#

# = or ==: string equal
$ [ "hello" = "hello" ] && echo "equal"
equal

# !=: string not equal
$ [ "hello" != "world" ] && echo "not equal"
not equal

# <: lexicographic less than (alphabetical order)
$ [ "apple" \< "banana" ] && echo "apple comes first"
apple comes first

# >: lexicographic greater than
$ [ "zebra" \> "apple" ] && echo "zebra comes later"
zebra comes later

# Note: In [ ... ], must escape < and > as \< and \>
# In [[ ... ]], don't need escaping
$ [[ "apple" < "banana" ]] && echo "works"
works

String comparison table:

Operator

Meaning

Example

Notes

= or ==

Equal

[ "a" = "a" ]

Both work, = is POSIX

!=

Not equal

[ "a" != "b" ]

True if different

<

Less (lex)

[ "a" \< "b" ]

In [ ] needs escaping

>

Greater (lex)

[ "z" \> "a" ]

In [ ] needs escaping

6.2.4. String Comparison Operators#

These operators compare strings (text):

6.2.4.1. The -eq, -ne, -lt, -le, -gt, -ge Operators#

Use these inside [ ... ] or [[ ... ]]:

# -eq: equal
$ [ 5 -eq 5 ] && echo "equal"
equal

# -ne: not equal
$ [ 5 -ne 3 ] && echo "not equal"
not equal

# -lt: less than
$ [ 3 -lt 5 ] && echo "yes"
yes

# -le: less than or equal
$ [ 5 -le 5 ] && echo "yes"
yes

# -gt: greater than
$ [ 10 -gt 5 ] && echo "yes"
yes

# -ge: greater than or equal
$ [ 5 -ge 5 ] && echo "yes"
yes

All comparison operators:

Operator

Meaning

Example

Result

-eq

Equal

[ 5 -eq 5 ]

true

-ne

Not equal

[ 5 -ne 3 ]

true

-lt

Less than

[ 3 -lt 5 ]

true

-le

Less or equal

[ 5 -le 5 ]

true

-gt

Greater than

[ 10 -gt 5 ]

true

-ge

Greater or equal

[ 5 -ge 5 ]

true

6.2.5. Numeric Comparison Operators#

These operators compare numbers (integers) and return true (0) or false (1):