Conditionals#

Concepts#

The if Statement#

if condition; then
    commands
fi
if condition; then
    commands
elif other_condition; then
    other_commands
else
    fallback_commands
fi

Example:

if [ -f /etc/hosts ]; then
    echo "/etc/hosts exists"
else
    echo "/etc/hosts not found"
fi

Test Commands: [ ] vs [[ ]]#

There are two ways to write conditions:

[ ] — POSIX Test (compatible with sh)#

if [ "$a" = "$b" ]; then ...

[ is actually a command (try which [). It requires:

  • Spaces after [ and before ]
  • Quoting variables (unquoted empty variables cause syntax errors)
  • Escaping < and > operators

[[ ]] — Bash Extended Test (preferred)#

if [[ "$a" == "$b" ]]; then ...

[[ is a Bash built-in with advantages:

  • No word splitting — unquoted variables are safer (but still quote by habit)
  • Supports && and || inside
  • Supports pattern matching with ==
  • Supports regex matching with =~
  • No need to escape < and >

Recommendation: Use [[ ]] in Bash scripts. Use [ ] only if you need POSIX compatibility (#!/bin/sh).

String Comparisons#

Operator [[ ]] [ ] Meaning
Equal == = Strings are identical
Not equal != != Strings differ
Less than < \< Alphabetically before
Greater than > \> Alphabetically after
Empty -z "$var" -z "$var" String is empty (zero length)
Not empty -n "$var" -n "$var" String is not empty
name="Alice"

if [[ "$name" == "Alice" ]]; then
    echo "Hello Alice!"
fi

if [[ -z "$unset_var" ]]; then
    echo "Variable is empty or not set"
fi

if [[ -n "$name" ]]; then
    echo "Name is set to: $name"
fi

Numeric Comparisons#

For numbers, use these operators (same in [ ] and [[ ]]):

Operator Meaning
-eq Equal
-ne Not equal
-lt Less than
-le Less than or equal
-gt Greater than
-ge Greater than or equal
age=25

if [[ $age -ge 18 ]]; then
    echo "Adult"
else
    echo "Minor"
fi

count=$(ls /etc | wc -l)
if [[ $count -gt 100 ]]; then
    echo "More than 100 files in /etc"
fi

Inside (( )) (arithmetic context), you can use C-style operators:

if (( age >= 18 )); then
    echo "Adult"
fi

if (( count > 100 && count < 500 )); then
    echo "Between 100 and 500"
fi

File Tests#

Operator True If
-f file File exists and is a regular file
-d dir Directory exists
-e path Path exists (any type)
-r file File is readable
-w file File is writable
-x file File is executable
-s file File exists and is not empty
-L file File is a symbolic link
file1 -nt file2 file1 is newer than file2
file1 -ot file2 file1 is older than file2
if [[ -f /etc/hosts ]]; then
    echo "File exists"
fi

if [[ -d /tmp ]]; then
    echo "/tmp is a directory"
fi

if [[ -x ./myscript.sh ]]; then
    echo "Script is executable"
fi

if [[ ! -f /etc/missing ]]; then
    echo "File does not exist"
fi

Logical Operators#

Inside [[ ]]:#

if [[ $a -gt 0 && $a -lt 100 ]]; then
    echo "a is between 1 and 99"
fi

if [[ $x == "yes" || $x == "y" ]]; then
    echo "Affirmative"
fi

if [[ ! -f /tmp/lockfile ]]; then
    echo "No lock file"
fi

Outside [[ ]] (combining commands):#

if [ "$a" -gt 0 ] && [ "$a" -lt 100 ]; then
    echo "a is between 1 and 99"
fi

case Statement#

For multiple options, case is cleaner than chained if/elif:

case "$variable" in
    pattern1)
        commands
        ;;
    pattern2|pattern3)
        commands
        ;;
    *)
        default_commands
        ;;
esac
read -p "Enter a fruit: " fruit

case "$fruit" in
    apple)
        echo "Red or green"
        ;;
    banana|plantain)
        echo "Yellow"
        ;;
    grape)
        echo "Purple"
        ;;
    *)
        echo "Unknown fruit: $fruit"
        ;;
esac

Patterns support wildcards:

case "$filename" in
    *.txt)   echo "Text file" ;;
    *.jpg|*.png) echo "Image file" ;;
    *.sh)    echo "Shell script" ;;
    *)       echo "Unknown type" ;;
esac

Pattern Matching in [[ ]]#

# Glob pattern
if [[ "$file" == *.txt ]]; then
    echo "Text file"
fi

# Regex matching
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "Valid email format"
fi

Lab#

Exercise 1: Basic if/else#

cd ~/lab/scripts

cat > checkfile.sh << 'SCRIPT'
#!/bin/bash
# Check if a file exists

FILE="${1:-/etc/hosts}"

if [[ -f "$FILE" ]]; then
    echo "File '$FILE' exists."
    echo "Size: $(wc -c < "$FILE") bytes"
    echo "Lines: $(wc -l < "$FILE")"
else
    echo "File '$FILE' does not exist."
    exit 1
fi
SCRIPT

chmod +x checkfile.sh
./checkfile.sh
./checkfile.sh /etc/passwd
./checkfile.sh /nonexistent

Exercise 2: Numeric Comparisons#

cd ~/lab/scripts

cat > diskcheck.sh << 'SCRIPT'
#!/bin/bash
# Check disk usage and warn if high

usage=$(df / | tail -1 | awk '{print $5}' | tr -d '%')

echo "Root filesystem usage: ${usage}%"

if [[ $usage -ge 90 ]]; then
    echo "CRITICAL: Disk almost full!"
elif [[ $usage -ge 75 ]]; then
    echo "WARNING: Disk usage is high."
elif [[ $usage -ge 50 ]]; then
    echo "INFO: Disk usage is moderate."
else
    echo "OK: Plenty of space available."
fi
SCRIPT

chmod +x diskcheck.sh
./diskcheck.sh

Exercise 3: String Comparisons and Input#

cd ~/lab/scripts

cat > yesno.sh << 'SCRIPT'
#!/bin/bash
# Yes/No prompt

read -p "Do you want to continue? (yes/no): " answer

case "${answer,,}" in   # ${answer,,} converts to lowercase
    yes|y)
        echo "Continuing..."
        ;;
    no|n)
        echo "Aborting."
        exit 0
        ;;
    *)
        echo "Invalid answer: $answer"
        exit 1
        ;;
esac

echo "Proceeding with the operation."
SCRIPT

chmod +x yesno.sh
./yesno.sh

Exercise 4: File Type Checker#

cd ~/lab/scripts

cat > filetype.sh << 'SCRIPT'
#!/bin/bash
# Check file type using tests

if [[ $# -eq 0 ]]; then
    echo "Usage: $0 <path>"
    exit 1
fi

path="$1"

if [[ ! -e "$path" ]]; then
    echo "'$path' does not exist."
    exit 1
fi

echo "Checking: $path"

if [[ -f "$path" ]]; then
    echo "  Type: regular file"
elif [[ -d "$path" ]]; then
    echo "  Type: directory"
elif [[ -L "$path" ]]; then
    echo "  Type: symbolic link"
else
    echo "  Type: other"
fi

[[ -r "$path" ]] && echo "  Readable: yes" || echo "  Readable: no"
[[ -w "$path" ]] && echo "  Writable: yes" || echo "  Writable: no"
[[ -x "$path" ]] && echo "  Executable: yes" || echo "  Executable: no"

if [[ -f "$path" && -s "$path" ]]; then
    echo "  Size: $(wc -c < "$path") bytes"
elif [[ -f "$path" ]]; then
    echo "  Size: empty"
fi
SCRIPT

chmod +x filetype.sh
./filetype.sh /etc/hosts
./filetype.sh /tmp
./filetype.sh /bin/bash
./filetype.sh /nonexistent

Exercise 5: case with File Extensions#

cd ~/lab/scripts

cat > classify.sh << 'SCRIPT'
#!/bin/bash
# Classify a file by extension

if [[ $# -eq 0 ]]; then
    echo "Usage: $0 <filename>"
    exit 1
fi

case "$1" in
    *.txt|*.md|*.csv)
        echo "$1: Text/data file"
        ;;
    *.sh|*.bash)
        echo "$1: Shell script"
        ;;
    *.py)
        echo "$1: Python script"
        ;;
    *.jpg|*.jpeg|*.png|*.gif|*.svg)
        echo "$1: Image file"
        ;;
    *.tar.gz|*.tgz|*.zip|*.tar.xz)
        echo "$1: Archive"
        ;;
    *.log)
        echo "$1: Log file"
        ;;
    *)
        echo "$1: Unknown type"
        ;;
esac
SCRIPT

chmod +x classify.sh
./classify.sh report.txt
./classify.sh photo.jpg
./classify.sh backup.tar.gz
./classify.sh script.py
./classify.sh mystery.xyz

Review#

1. What is the difference between `[ ]` and `[[ ]]`?

[ ] is the POSIX test command — portable but requires careful quoting and escaping. [[ ]] is a Bash built-in that supports &&/|| inside, pattern matching, regex, and is safer with unquoted variables. Use [[ ]] in Bash scripts.

2. How do you compare numbers in a conditional?

Use -eq, -ne, -lt, -le, -gt, -ge inside [[ ]] or [ ]. Or use (( )) for C-style: if (( a > b )); then.

3. How do you check if a file exists and is readable?

if [[ -f "$file" && -r "$file" ]]; then-f checks if it is a regular file, -r checks if it is readable.

4. What does the `!` operator do in a test?

It negates the condition. if [[ ! -f "$file" ]] is true when the file does NOT exist.

5. When should you use `case` instead of `if/elif`?

When comparing a single variable against multiple possible values. case is cleaner and more readable than a chain of elif statements for this purpose.

6. What does `-z` test?

Whether a string is empty (zero length). [[ -z "$var" ]] is true if $var is empty or unset. Its opposite is -n (not empty).


Previous: Variables and Quoting | Next: Loops