if and test conditions

Bash · Essentials · 12 min

Checking that files exist, arguments are present or values match is the core of every script.

if runs a command and takes the branch when its exit status is 0. The command is usually a test:

  • [ ... ] is the classic POSIX test command (it is literally a program named [), so it needs spaces around brackets and quotes around variables.
  • [[ ... ]] is a bash keyword: it does not word-split variables, supports && and || inside, == with glob patterns and =~ with regular expressions. Prefer it in bash scripts.
  • (( ... )) evaluates arithmetic and succeeds when the result is non-zero.

Common tests: strings -z (empty), -n (non-empty), ==, !=; numbers -eq -ne -lt -le -gt -ge (or < and > inside (( ))); files -e exists, -f regular file, -d directory, -r -w -x permissions, -s non-empty.

The syntax is if cond; then ...; elif cond; then ...; else ...; fi. Any command works as a condition: if grep -q error log.txt; then is idiomatic and faster than capturing output.

Watch out: inside [[ ]], < and > compare strings alphabetically, so [[ 10 < 9 ]] is true. Use -lt or (( )) for numbers. After an =~ match, captured groups are in the BASH_REMATCH array.

Example

f=/etc/passwd
if [[ -d $f ]]; then
  echo "$f is a directory"
elif [[ -f $f && -r $f ]]; then
  echo "$f is a readable file"     # this branch runs
else
  echo "$f: not found"
fi
name=""
[[ -z $name ]] && echo "name is empty"            # name is empty
n=7
if (( n > 5 )); then echo "big"; fi              # big
if [ "$n" -lt 10 ]; then echo "single digit"; fi # single digit
s="report.csv"
[[ $s == *.csv ]] && echo "CSV file"             # CSV file
[[ $s =~ ^([a-z]+)\. ]] && echo "${BASH_REMATCH[1]}"   # report
[[ 10 < 9 ]] && echo "string comparison!"        # string comparison!

Remember

  • if takes the branch when the condition command exits 0
  • Prefer [[ ]] for strings and files, (( )) for numbers
  • Any command can be a condition: if grep -q ...; then

Watch out for

  • Comparing numbers with < or == inside [[ ]], which compares them as strings.

Check yourself

  1. What does `[[ 10 < 9 ]]` return?
  2. Which test is true when $dir names an existing directory?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs