Guide Bash-Scripting Beginner

Operators

Arithmetic, comparison, logical, and file test operators in Bash — and the crucial difference between [ ], [[ ]], and (( )).

4 min read

Arithmetic Operators

A=10
B=3

echo $((A + B))    # 13
echo $((A - B))      # 7
echo $((A * B))        # 30
echo $((A / B))          # 3  — integer division, no decimals!
echo $((A % B))            # 1  — modulo (remainder)
echo $((A ** 2))              # 100 — exponentiation

((A++))     # increment A in place
((A += 5))    # A = A + 5

For non-integer math, Bash has no native support — use bc or awk:

echo "10 / 3" | bc -l          # 3.33333333333333333333
awk "BEGIN {print 10/3}"          # 3.33333

Comparison Operators

# Numeric comparisons — use inside (( )) or [ ]/[[ ]] with these operators:
[ "$A" -eq "$B" ]     # equal
[ "$A" -ne "$B" ]       # not equal
[ "$A" -gt "$B" ]         # greater than
[ "$A" -lt "$B" ]           # less than
[ "$A" -ge "$B" ]             # greater or equal
[ "$A" -le "$B" ]               # less or equal

((A > B))    # arithmetic context — use regular >, <, ==, != instead
# String comparisons — DIFFERENT operators than numeric!
[ "$STR1" = "$STR2" ]     # equal
[ "$STR1" != "$STR2" ]      # not equal
[[ "$STR1" > "$STR2" ]]       # lexical greater-than (needs [[ ]], not [ ])

Classic trap: using -eq on strings or = on numbers gives wrong or confusing results — the operator families are not interchangeable.

Logical Operators

if [ "$A" -gt 5 ] && [ "$B" -lt 10 ]; then echo "both true"; fi
if [[ $A -gt 5 && $B -lt 10 ]]; then echo "both true, inside [[ ]]"; fi

if [ "$A" -gt 5 ] || [ "$B" -gt 5 ]; then echo "at least one true"; fi

if ! [ "$A" -eq "$B" ]; then echo "not equal"; fi

# Short-circuit command chaining (not just inside test brackets)
mkdir -p /tmp/app && cd /tmp/app          # run cd ONLY if mkdir succeeded
grep -q "error" log.txt || echo "no errors found"   # run echo ONLY if grep found NOTHING

File Test Operators

[ -f "$FILE" ]     # is a regular file
[ -d "$DIR" ]        # is a directory
[ -e "$PATH" ]         # exists (any type)
[ -r "$FILE" ]           # is readable
[ -w "$FILE" ]             # is writable
[ -x "$FILE" ]               # is executable
[ -s "$FILE" ]                 # exists AND is non-empty
[ -L "$FILE" ]                   # is a symbolic link
if [ ! -f "$CONFIG_FILE" ]; then
    echo "Config file missing: $CONFIG_FILE" >&2
    exit 1
fi

[ ] vs [[ ]] vs (( ))

ConstructPurposeNotes
[ ]POSIX test commandPortable to /bin/sh; requires careful quoting
[[ ]]Bash extended testSupports &&, `
(( ))Arithmetic evaluationFor numeric comparisons/math; no quoting needed inside
[[ "$STR" =~ ^[0-9]+$ ]]     # regex match — only works inside [[ ]]
(( A > B && C < D ))            # cleaner arithmetic logic than [ ] with -gt/-lt

Production Considerations

  • In portable scripts targeting /bin/sh (many CI systems, Alpine’s default shell), you’re restricted to [ ][[ ]] and (( )) are bash-only extensions.
  • Always quote variables inside [ ] ([ "$VAR" = "x" ]) — an unquoted, empty variable breaks the test’s argument count and throws a syntax error.
  • Prefer [[ ]] in bash-only scripts for its safety (no word-splitting on unquoted variables) and extra features like regex matching.

Quick Interview Answer

“Bash arithmetic uses $(( )) or (( )); numeric string comparisons in [ ] use -eq/-gt/-lt, while string equality uses =/!= — mixing these up is a classic bug. [[ ]] is the safer, bash-only extended test supporting regex and pattern matching without needing to quote every variable, while [ ] is the portable POSIX equivalent needed for /bin/sh scripts.”

Common Mistakes

  • Using -eq to compare strings, or = to compare numbers.
  • Forgetting to quote variables inside [ ], causing “unary operator expected” errors on empty values.
  • Assuming [[ ]] works in a script shebanged #!/bin/sh — it doesn’t; dash and other POSIX shells will error on it.

Add More Questions to This Guide

Know a question that should be here? Share it and help the community!

Open Google Form