Bash Cheatsheet

Tests and Conditionals

Use this Bash reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

[ ] — POSIX test Command

# Equivalent forms
test -f file.txt
[ -f file.txt ]          # spaces around brackets are required

# IMPORTANT: always quote variables
[ "$var" = "value" ]     # correct
[ $var = value ]         # WRONG: breaks if $var is empty or has spaces

# Negate
[ ! -f file.txt ]

# Combine with -a (and), -o (or) — deprecated, use && / || instead
[ -f file -a -r file ]   # avoid
[ -f file ] && [ -r file ]  # preferred

[[ ]] — Bash Extended Test

# Double brackets: safer, more features
[[ -f file.txt ]]
[[ $var == "value" ]]    # no quoting required for $var
[[ $var != "other" ]]

# Logical operators (not -a / -o)
[[ $a -gt 0 && $b -lt 10 ]]
[[ $x == "yes" || $x == "y" ]]
[[ ! -d /tmp/missing ]]

# Glob matching (only in [[ ]])
[[ $file == *.txt ]]
[[ $name == foo* ]]
[[ $name != *bar ]]

# Regex matching (=~ only in [[ ]])
[[ $str =~ ^[0-9]+$ ]]
[[ $email =~ @[a-zA-Z]+\.[a-zA-Z]{2,} ]]
# Capture groups available in BASH_REMATCH
[[ "2024-01-15" =~ ([0-9]{4})-([0-9]{2})-([0-9]{2}) ]]
echo "${BASH_REMATCH[1]}"   # 2024
echo "${BASH_REMATCH[2]}"   # 01
echo "${BASH_REMATCH[3]}"   # 15

(( )) — Arithmetic Test

(( 1 + 1 == 2 ))         # true (exit 0)
(( 0 ))                  # false (exit 1)
(( 1 ))                  # true (exit 0)
(( a > b ))              # no $ needed inside (( ))
(( a++ ))                # side effect: increments a
(( a = b + c ))          # assignment inside arithmetic test

if (( count > 0 )); then
  echo "positive"
fi

# Arithmetic boolean
(( x > 0 && y < 10 ))
(( x == 0 || y != 0 ))
(( ! flag ))

File Test Operators

OperatorTrue when
-e filefile/dir/link exists
-f fileregular file exists
-d filedirectory exists
-L filesymbolic link exists
-r filereadable
-w filewritable
-x fileexecutable
-s fileexists and size > 0
-b fileblock device
-c filecharacter device
-p filenamed pipe (FIFO)
-S filesocket
-t fdfile descriptor is open terminal
-N filemodified since last read
-O fileowned by current user
-G filegroup-owned by current user's group
f1 -nt f2f1 newer than f2 (mtime)
f1 -ot f2f1 older than f2 (mtime)
f1 -ef f2same file (hard links)
[[ -f /etc/passwd && -r /etc/passwd ]] && cat /etc/passwd
[[ -d "$dir" ]] || mkdir -p "$dir"
[[ -x "$(command -v git)" ]] && echo "git available"
[[ -t 1 ]] && echo "stdout is a terminal"   # useful for coloring output

String Test Operators

OperatorTrue when
-z strstring is empty (zero length)
-n strstring is non-empty
s1 = s2equal (POSIX [ ])
s1 == s2equal (Bash [[ ]])
s1 != s2not equal
s1 < s2s1 sorts before s2 (lexicographic)
s1 > s2s1 sorts after s2
[[ -z "$var" ]] && echo "var is empty"
[[ -n "$1" ]] || { echo "usage: $0 <arg>"; exit 1; }
[[ "$name" == "root" ]] && echo "running as root"

Integer Test Operators

OperatorMeaning
n1 -eq n2equal
n1 -ne n2not equal
n1 -lt n2less than
n1 -le n2less than or equal
n1 -gt n2greater than
n1 -ge n2greater than or equal
[[ $count -gt 0 ]] && echo "non-zero"
[[ $exit_code -ne 0 ]] && echo "failed"
(( count > 0 )) && echo "non-zero"  # arithmetic form (preferred)

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

Feature[ ][[ ]](( ))
POSIXyesnono
Glob matchingnoyesno
Regex matchingnoyes (=~)no
No quote needednoyesyes
Arithmetic-eq/-lt etc.-eq/-lt etc.C operators
Logical ops-a/-o&& / ||&& / ||
Word splittingyesnono
# Prefer [[ ]] for strings, (( )) for numbers
[[ -f "$file" && -n "$content" ]] && echo "ok"
(( x > 0 && y < 100 )) && echo "in range"

Variable / Option Tests

# Check if variable is set (even if empty)
[[ -v varname ]]           # Bash 4.2+
[[ ${varname+x} ]]        # portable alternative

# Check if array element is set
[[ -v arr[3] ]]

# Check if shell option is set
[[ -o errexit ]]           # true if set -e is active
[[ $- == *e* ]]            # same, POSIX-ish

# Check if a function is defined
declare -f funcname > /dev/null && echo "defined"

# Check if command exists
command -v git > /dev/null && echo "git installed"
type -t git > /dev/null   # prints "file", "alias", "builtin", or "function"

Chaining Tests

# Short-circuit — common idioms
[[ -f file ]] && echo "exists"
[[ -d dir ]] || mkdir dir
command_that_fails || exit 1

# Combine file and string tests
[[ -f "$config" && -r "$config" && -s "$config" ]] || die "bad config"

# Check multiple programs
for cmd in git curl jq; do
  command -v "$cmd" > /dev/null || { echo "missing: $cmd"; exit 1; }
done

BASH_REMATCH — Regex Captures

str="Date: 2024-12-25, Time: 14:30"
regex='([0-9]{4}-[0-9]{2}-[0-9]{2})'

if [[ $str =~ $regex ]]; then
  echo "Full match: ${BASH_REMATCH[0]}"   # "2024-12-25"
  echo "Group 1:    ${BASH_REMATCH[1]}"   # "2024-12-25"
fi

# Multiple groups
if [[ $str =~ ([0-9]{4})-([0-9]{2})-([0-9]{2}) ]]; then
  year="${BASH_REMATCH[1]}"   # 2024
  mon="${BASH_REMATCH[2]}"    # 12
  day="${BASH_REMATCH[3]}"    # 25
fi

# Note: store regex in variable to avoid quoting issues with [[ =~ ]]