Conditionals
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