Shell Script Conditionals: Complete Guide
Table of Contents
1. [Introduction to Conditionals](#introduction-to-conditionals) 2. [Basic Conditional Syntax](#basic-conditional-syntax) 3. [Test Commands and Operators](#test-commands-and-operators) 4. [Conditional Statements](#conditional-statements) 5. [Advanced Conditional Constructs](#advanced-conditional-constructs) 6. [Best Practices](#best-practices) 7. [Common Use Cases](#common-use-cases) 8. [Troubleshooting](#troubleshooting)Introduction to Conditionals
Conditionals in shell scripts are fundamental constructs that allow scripts to make decisions based on various conditions. They enable programs to execute different code paths depending on the evaluation of expressions, file states, variable values, or command exit statuses. Shell conditionals form the backbone of logic flow control in bash scripting.
The shell provides several mechanisms for implementing conditional logic, including the if statement, case statement, and logical operators. These constructs work by evaluating test conditions that return either true (exit status 0) or false (non-zero exit status).
Basic Conditional Syntax
The if Statement Structure
The basic if statement in shell scripting follows this syntax:
`bash
if [ condition ]; then
# commands to execute if condition is true
fi
`
The extended form includes else and elif clauses:
`bash
if [ condition1 ]; then
# commands for condition1
elif [ condition2 ]; then
# commands for condition2
else
# commands if no conditions are true
fi
`
Command Notes
- The [ is actually a command (alias for test)
- Spaces around brackets are mandatory
- The then keyword can be on the same line with a semicolon or on a new line
- Each if must have a corresponding fi
- The elif clause allows multiple condition testing
Test Commands and Operators
File Test Operators
File test operators check various properties of files and directories:
| Operator | Description | Example |
|----------|-------------|---------|
| -e | File exists | [ -e /path/file ] |
| -f | Regular file exists | [ -f /path/file ] |
| -d | Directory exists | [ -d /path/dir ] |
| -r | File is readable | [ -r /path/file ] |
| -w | File is writable | [ -w /path/file ] |
| -x | File is executable | [ -x /path/file ] |
| -s | File exists and is not empty | [ -s /path/file ] |
| -L | File is a symbolic link | [ -L /path/link ] |
| -O | File is owned by current user | [ -O /path/file ] |
| -G | File group matches current user | [ -G /path/file ] |
String Comparison Operators
String operators compare text values and check string properties:
| Operator | Description | Example |
|----------|-------------|---------|
| = or == | Strings are equal | [ "$str1" = "$str2" ] |
| != | Strings are not equal | [ "$str1" != "$str2" ] |
| < | String1 is less than string2 | [ "$str1" \< "$str2" ] |
| > | String1 is greater than string2 | [ "$str1" \> "$str2" ] |
| -z | String is empty | [ -z "$string" ] |
| -n | String is not empty | [ -n "$string" ] |
Numeric Comparison Operators
Numeric operators perform mathematical comparisons:
| Operator | Description | Example |
|----------|-------------|---------|
| -eq | Equal to | [ $num1 -eq $num2 ] |
| -ne | Not equal to | [ $num1 -ne $num2 ] |
| -lt | Less than | [ $num1 -lt $num2 ] |
| -le | Less than or equal | [ $num1 -le $num2 ] |
| -gt | Greater than | [ $num1 -gt $num2 ] |
| -ge | Greater than or equal | [ $num1 -ge $num2 ] |
Logical Operators
Logical operators combine multiple conditions:
| Operator | Description | Example |
|----------|-------------|---------|
| && | Logical AND | [ condition1 ] && [ condition2 ] |
| \|\| | Logical OR | [ condition1 ] \|\| [ condition2 ] |
| ! | Logical NOT | [ ! condition ] |
| -a | AND (within test) | [ condition1 -a condition2 ] |
| -o | OR (within test) | [ condition1 -o condition2 ] |
Conditional Statements
Basic if-then-else Examples
Here are practical examples demonstrating various conditional constructs:
`bash
#!/bin/bash
Simple file existence check
if [ -f "/etc/passwd" ]; then echo "Password file exists" else echo "Password file not found" fiMultiple conditions with elif
read -p "Enter your age: " ageif [ $age -lt 13 ]; then echo "You are a child" elif [ $age -lt 20 ]; then echo "You are a teenager" elif [ $age -lt 60 ]; then echo "You are an adult" else echo "You are a senior" fi
String comparison
read -p "Enter your name: " usernameif [ "$username" = "admin" ]; then
echo "Welcome administrator"
elif [ -z "$username" ]; then
echo "No username provided"
else
echo "Hello $username"
fi
`
Complex Conditional Logic
`bash
#!/bin/bash
File permission and existence check
filename="/path/to/file"if [ -e "$filename" ]; then if [ -r "$filename" ] && [ -w "$filename" ]; then echo "File exists and is readable and writable" elif [ -r "$filename" ]; then echo "File exists and is readable only" else echo "File exists but no read permissions" fi else echo "File does not exist" fi
Combining multiple test conditions
user_input="test" number=42if [ -n "$user_input" ] && [ $number -gt 40 ]; then echo "Both conditions are true" fi
Using logical NOT
if [ ! -d "/nonexistent" ]; then echo "Directory does not exist" fi`Case Statement
The case statement provides an alternative to multiple elif conditions:
`bash
#!/bin/bash
read -p "Enter a letter: " letter
case $letter in
[aeiou]|[AEIOU])
echo "You entered a vowel"
;;
[0-9])
echo "You entered a number"
;;
[a-zA-Z])
echo "You entered a consonant"
;;
*)
echo "You entered a special character"
;;
esac
`
Case Statement Patterns
| Pattern Type | Example | Description |
|--------------|---------|-------------|
| Literal | "hello") | Exact string match |
| Wildcard | *.txt) | Pattern matching |
| Character class | [0-9]) | Range of characters |
| Multiple patterns | yes\|y\|Y) | OR conditions |
| Default | *) | Catch-all pattern |
Advanced Conditional Constructs
Double Bracket Syntax
The [[ construct provides enhanced conditional testing:
`bash
#!/bin/bash
Pattern matching with double brackets
string="hello world"if [[ $string == "world" ]]; then echo "String contains 'world'" fi
Regular expression matching
if [[ $string =~ ^hello ]]; then echo "String starts with 'hello'" fiEmpty variable check without quotes
if [[ -z $variable ]]; then echo "Variable is empty" fi`Arithmetic Evaluation
Using (( for arithmetic conditions:
`bash
#!/bin/bash
num1=10 num2=20
Arithmetic comparison
if (( num1 < num2 )); then echo "$num1 is less than $num2" fiComplex arithmetic
if (( (num1 + num2) > 25 )); then echo "Sum is greater than 25" fiIncrement and test
counter=0 if (( ++counter == 1 )); then echo "Counter incremented to 1" fi`Conditional Assignment
`bash
#!/bin/bash
Default value assignment
username=${1:-"anonymous"} echo "Username: $username"Conditional variable setting
debug_mode=${DEBUG:-false}if [ "$debug_mode" = "true" ]; then echo "Debug mode enabled" fi
Parameter expansion with conditions
config_file=${CONFIG_FILE:="/etc/default.conf"} echo "Using config file: $config_file"`Short-Circuit Evaluation
`bash
#!/bin/bash
Command execution based on conditions
[ -f "important.txt" ] && echo "File found" || echo "File missing"Create directory if it doesn't exist
[ ! -d "logs" ] && mkdir logsBackup file if it exists
[ -f "data.txt" ] && cp data.txt data.txt.backupChain multiple commands
[ -x "/usr/bin/git" ] && [ -d ".git" ] && echo "Git repository detected"`Best Practices
Quoting Variables
Always quote variables to prevent word splitting and pathname expansion:
`bash
Correct
if [ "$variable" = "value" ]; then echo "Match found" fiIncorrect - may fail with spaces in variable
if [ $variable = "value" ]; then echo "This might fail" fi`Error Handling
`bash
#!/bin/bash
Check command line arguments
if [ $# -eq 0 ]; then echo "Usage: $0filename="$1"
Validate file before processing
if [ ! -f "$filename" ]; then echo "Error: File '$filename' not found" exit 1 fiif [ ! -r "$filename" ]; then echo "Error: Cannot read file '$filename'" exit 1 fi
echo "Processing file: $filename"
`
Performance Considerations
`bash
#!/bin/bash
Use double brackets for better performance
if [[ -f "$file" && -r "$file" ]]; then # Process file cat "$file" fiAvoid unnecessary subprocess calls
Instead of: if [ $(wc -l < file) -gt 100 ]
Use: if [[ $(wc -l < file) -gt 100 ]]
Cache expensive operations
file_count=$(find /path -type f | wc -l) if [[ $file_count -gt 1000 ]]; then echo "Many files found: $file_count" fi`Common Use Cases
System Administration Scripts
`bash
#!/bin/bash
Disk space monitoring
threshold=90 usage=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')if [ $usage -gt $threshold ]; then echo "WARNING: Disk usage is ${usage}%" # Send alert or cleanup fi
Service status check
if systemctl is-active --quiet nginx; then echo "Nginx is running" else echo "Starting nginx service" systemctl start nginx fiLog file rotation
log_file="/var/log/application.log" max_size=10485760 # 10MBif [ -f "$log_file" ] && [ $(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file") -gt $max_size ]; then
mv "$log_file" "${log_file}.old"
touch "$log_file"
echo "Log file rotated"
fi
`
User Input Validation
`bash
#!/bin/bash
validate_email() { local email="$1" if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then return 0 else return 1 fi }
read -p "Enter your email: " user_email
if validate_email "$user_email"; then echo "Valid email address" else echo "Invalid email format" exit 1 fi
Numeric input validation
read -p "Enter a number between 1 and 100: " numberif ! [[ "$number" =~ ^[0-9]+$ ]]; then
echo "Error: Not a valid number"
exit 1
elif [ $number -lt 1 ] || [ $number -gt 100 ]; then
echo "Error: Number out of range"
exit 1
else
echo "Valid number: $number"
fi
`
Configuration Management
`bash
#!/bin/bash
config_file="/etc/myapp/config.conf" backup_dir="/etc/myapp/backups"
Ensure backup directory exists
if [ ! -d "$backup_dir" ]; then mkdir -p "$backup_dir" fiBackup existing config
if [ -f "$config_file" ]; then timestamp=$(date +%Y%m%d_%H%M%S) cp "$config_file" "$backup_dir/config.conf.$timestamp" echo "Configuration backed up" fiValidate new configuration
validate_config() { local config="$1" if [ ! -f "$config" ]; then echo "Config file not found" return 1 fi # Check for required parameters if ! grep -q "^database_host=" "$config"; then echo "Missing database_host parameter" return 1 fi return 0 }new_config="$1"
if [ -z "$new_config" ]; then
echo "Usage: $0
if validate_config "$new_config"; then
cp "$new_config" "$config_file"
echo "Configuration updated successfully"
else
echo "Configuration validation failed"
exit 1
fi
`
Troubleshooting
Common Errors and Solutions
| Error | Cause | Solution |
|-------|-------|----------|
| [: missing ] | Missing closing bracket | Add closing bracket with proper spacing |
| [: too many arguments | Unquoted variable with spaces | Quote the variable: "$var" |
| command not found: [[ | Using [[ in non-bash shell | Use [ or ensure bash interpreter |
| integer expression expected | Non-numeric comparison with numeric operators | Validate input or use string operators |
Debugging Conditionals
`bash
#!/bin/bash
Enable debug mode
set -xTest variable content
variable="test value" echo "Variable contains: '$variable'" echo "Variable length: ${#variable}"Test condition step by step
if [ -n "$variable" ]; then echo "Variable is not empty" fiDisable debug mode
set +xManual debugging
debug=true if [ "$debug" = "true" ]; then echo "Debug: Testing file existence" ls -la "$filename" 2>/dev/null || echo "File not accessible" fi`Testing Conditionals
`bash
#!/bin/bash
Function to test conditions
test_condition() { local description="$1" local condition="$2" echo -n "Testing: $description ... " if eval "$condition"; then echo "PASS" else echo "FAIL" fi }Run tests
test_condition "File exists" "[ -f '/etc/passwd' ]" test_condition "Number comparison" "[ 10 -gt 5 ]" test_condition "String equality" "[ 'hello' = 'hello' ]" test_condition "Variable is set" "[ -n '$HOME' ]"`Performance Testing
`bash
#!/bin/bash
Benchmark different conditional approaches
time_test() { local description="$1" local command="$2" local iterations=10000 echo "Testing: $description" time for ((i=0; iCompare single vs double brackets
time_test "Single brackets" '[ -f "/etc/passwd" ]' time_test "Double brackets" '[[ -f "/etc/passwd" ]]'Compare different string tests
string="test" time_test "String length with -n" '[ -n "$string" ]' time_test "String length with test" '[ ${#string} -gt 0 ]'`This comprehensive guide covers the essential aspects of using conditionals in shell scripts, from basic syntax to advanced techniques and real-world applications. The examples and explanations provide a solid foundation for implementing robust conditional logic in bash scripting environments.