Introduction to Conditional Statements
Conditional statements are fundamental to programming in Bash. They allow your scripts to make decisions based on conditions, execute different code paths, and handle various scenarios. The if...else construct is the primary way to implement conditional logic in Bash.
Key Concepts
- Exit Status: Every command returns a status (0 for success, non-zero for failure)
- Test Command:
[ ]or[[ ]]for evaluating conditions - Conditional Execution: Different paths based on condition results
- Nested Conditions: Multiple levels of decision-making
- Logical Operators: Combine multiple conditions
1. Basic If Statement Syntax
Simple If Statement
#!/bin/bash # Basic if statement if condition; then # commands to execute if condition is true echo "Condition is true" fi # Example with test command if [ "$name" = "John" ]; then echo "Hello John!" fi # Example with command exit status if grep -q "error" logfile.txt; then echo "Found errors in log file" fi # One-line if (not recommended for complex conditions) if [ -f "$file" ]; then echo "File exists"; fi
If-Else Statement
#!/bin/bash # Basic if-else if [ "$age" -ge 18 ]; then echo "You are an adult" else echo "You are a minor" fi # Example with multiple commands if cp file1.txt file2.txt; then echo "Copy successful" echo "File copied to file2.txt" else echo "Copy failed" >&2 exit 1 fi
If-Elif-Else Statement
#!/bin/bash # Multiple conditions with elif if [ "$score" -ge 90 ]; then echo "Grade: A" echo "Excellent work!" elif [ "$score" -ge 80 ]; then echo "Grade: B" echo "Good job!" elif [ "$score" -ge 70 ]; then echo "Grade: C" echo "Satisfactory" elif [ "$score" -ge 60 ]; then echo "Grade: D" echo "Needs improvement" else echo "Grade: F" echo "Failed" fi # Real-world example if [ "$1" = "start" ]; then systemctl start myservice echo "Service started" elif [ "$1" = "stop" ]; then systemctl stop myservice echo "Service stopped" elif [ "$1" = "restart" ]; then systemctl restart myservice echo "Service restarted" elif [ "$1" = "status" ]; then systemctl status myservice else echo "Usage: $0 {start|stop|restart|status}" exit 1 fi 2. Test Conditions and Operators
File Test Operators
#!/bin/bash file="document.txt" dir="/home/user" link="/path/to/symlink" # File existence and type if [ -e "$file" ]; then echo "File exists"; fi if [ -f "$file" ]; then echo "Regular file"; fi if [ -d "$dir" ]; then echo "Directory"; fi if [ -L "$link" ]; then echo "Symbolic link"; fi if [ -b "$file" ]; then echo "Block device"; fi if [ -c "$file" ]; then echo "Character device"; fi if [ -p "$file" ]; then echo "Named pipe"; fi if [ -S "$file" ]; then echo "Socket"; fi # File permissions if [ -r "$file" ]; then echo "Readable"; fi if [ -w "$file" ]; then echo "Writable"; fi if [ -x "$file" ]; then echo "Executable"; fi # File size if [ -s "$file" ]; then echo "File not empty"; fi # File ownership if [ -O "$file" ]; then echo "Owned by effective user"; fi if [ -G "$file" ]; then echo "Owned by effective group"; fi # File comparison if [ "$file1" -nt "$file2" ]; then echo "file1 newer than file2"; fi if [ "$file1" -ot "$file2" ]; then echo "file1 older than file2"; fi if [ "$file1" -ef "$file2" ]; then echo "Same file (hard links)"; fi # Examples backup_file="data.backup" if [ -f "$backup_file" ] && [ -s "$backup_file" ]; then echo "Backup exists and is not empty" fi config_dir="/etc/myapp" if [ -d "$config_dir" ] && [ -r "$config_dir" ] && [ -x "$config_dir" ]; then echo "Config directory accessible" fi
String Test Operators
#!/bin/bash str1="hello" str2="world" empty="" # String length if [ -z "$empty" ]; then echo "String is empty"; fi if [ -n "$str1" ]; then echo "String is not empty"; fi # String equality if [ "$str1" = "$str2" ]; then echo "Strings are equal"; fi if [ "$str1" != "$str2" ]; then echo "Strings are not equal"; fi # String comparison (lexicographic) if [ "$str1" \< "$str2" ]; then echo "$str1 comes before $str2"; fi if [ "$str1" \> "$str2" ]; then echo "$str1 comes after $str2"; fi # Pattern matching with [[ ]] if [[ "$filename" == *.txt ]]; then echo "Text file"; fi if [[ "$filename" == [a-z]* ]]; then echo "Starts with lowercase"; fi if [[ "$filename" == *[0-9]* ]]; then echo "Contains numbers"; fi # Regular expression matching if [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then echo "Valid email format" fi # Examples read -p "Enter yes/no: " answer if [[ "${answer,,}" =~ ^(yes|y)$ ]]; then echo "You agreed" elif [[ "${answer,,}" =~ ^(no|n)$ ]]; then echo "You disagreed" else echo "Invalid input" fi # Check if string contains substring if [[ "$str" == *"substring"* ]]; then echo "Contains substring" fi Numeric Test Operators
#!/bin/bash a=10 b=20 c=10 # Numeric comparisons if [ "$a" -eq "$c" ]; then echo "a equals c"; fi if [ "$a" -ne "$b" ]; then echo "a not equal to b"; fi if [ "$a" -lt "$b" ]; then echo "a less than b"; fi if [ "$a" -le "$c" ]; then echo "a less than or equal to c"; fi if [ "$b" -gt "$a" ]; then echo "b greater than a"; fi if [ "$b" -ge "$a" ]; then echo "b greater than or equal to a"; fi # Arithmetic evaluation with double parentheses if (( a == c )); then echo "a equals c"; fi if (( a != b )); then echo "a not equal to b"; fi if (( a < b )); then echo "a less than b"; fi if (( a <= c )); then echo "a less than or equal to c"; fi if (( b > a )); then echo "b greater than a"; fi if (( b >= a )); then echo "b greater than or equal to a"; fi # Complex arithmetic if (( (a + b) > 25 && (b - a) < 20 )); then echo "Complex condition met" fi # Examples score=85 if (( score >= 90 )); then grade="A" elif (( score >= 80 )); then grade="B" elif (( score >= 70 )); then grade="C" elif (( score >= 60 )); then grade="D" else grade="F" fi echo "Grade: $grade" # Modulo operation number=7 if (( number % 2 == 0 )); then echo "Even" else echo "Odd" fi
Logical Operators
#!/bin/bash # AND operator - both conditions must be true if [ -f "$file" ] && [ -r "$file" ]; then echo "File exists and is readable" fi # Using && with [[ ]] if [[ -f "$file" && -r "$file" ]]; then echo "File exists and is readable" fi # Multiple AND conditions if [ "$age" -ge 18 ] && [ "$age" -le 65 ] && [ "$citizen" = "yes" ]; then echo "Eligible to vote" fi # OR operator - at least one condition must be true if [ "$day" = "Saturday" ] || [ "$day" = "Sunday" ]; then echo "It's the weekend!" fi # Using || with [[ ]] if [[ "$day" == "Saturday" || "$day" == "Sunday" ]]; then echo "Weekend!" fi # Combining AND and OR if [ -f "$file" ] && ( [ -r "$file" ] || [ -w "$file" ] ); then echo "File exists and is either readable or writable" fi # NOT operator if [ ! -f "$file" ]; then echo "File does not exist" fi if [[ ! -d "$dir" ]]; then echo "Directory does not exist" fi if ! command -v git &> /dev/null; then echo "Git is not installed" exit 1 fi # Complex conditions if { [ -f "$file" ] || [ -d "$file" ]; } && [ -r "$file" ]; then echo "Path exists (file or directory) and is readable" fi # Examples user_input="yes" if [[ "${user_input,,}" =~ ^(y|yes)$ ]]; then echo "User confirmed" fi # Check multiple conditions required_tools=("git" "make" "gcc") missing_tools=() for tool in "${required_tools[@]}"; do if ! command -v "$tool" &> /dev/null; then missing_tools+=("$tool") fi done if [ ${#missing_tools[@]} -eq 0 ]; then echo "All required tools are installed" else echo "Missing tools: ${missing_tools[*]}" fi 3. Advanced If Statements
Using Double Brackets [[ ]]
#!/bin/bash # [[ ]] is bash-specific, more powerful than [ ] # Pattern matching if [[ "$filename" == *.txt ]]; then echo "Text file" fi if [[ "$filename" == [a-z]*.txt ]]; then echo "Text file starting with lowercase" fi # Regular expression matching if [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then echo "Valid email" fi # No word splitting or pathname expansion var="a b c" if [[ $var == "a b c" ]]; then # No quotes needed echo "Matches" fi # Safer with empty variables unset var if [[ -n $var ]]; then # No error if var is unset echo "var is set" fi # Combining conditions if [[ -f "$file" && -r "$file" || -w "$file" ]]; then echo "File exists and is readable or writable" fi # Parentheses for grouping if [[ (-f "$file" || -d "$file") && -r "$file" ]]; then echo "Path exists (file or dir) and is readable" fi # Examples ip="192.168.1.100" if [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then echo "Valid IP address format" fi path="/home/user/docs/file.txt" if [[ $path == *.txt && -f $path ]]; then echo "Text file exists" fi Using Double Parentheses (( ))
#!/bin/bash # (( )) for arithmetic evaluation # Basic arithmetic if (( a > b )); then echo "a is greater than b" fi # Complex expressions if (( (a + b) * c > 100 )); then echo "Result > 100" fi # Increment/Decrement counter=5 if (( counter++ > 5 )); then echo "Counter was > 5 before increment" else echo "Counter was <= 5 before increment, now: $counter" fi # Bitwise operations flags=0b1010 if (( flags & 0b0010 )); then echo "Bit 1 is set" fi # Ternary-like expression max=$(( a > b ? a : b )) # Multiple conditions if (( a > b && c < d || e == f )); then echo "Complex condition true" fi # Examples number=42 if (( number % 2 == 0 )); then echo "$number is even" fi year=2024 if (( year % 400 == 0 )) || (( year % 4 == 0 && year % 100 != 0 )); then echo "$year is a leap year" fi total=95 passed=85 if (( total >= passed )); then echo "Passed" else echo "Failed" fi
Nested If Statements
#!/bin/bash # Basic nesting if [ -f "$file" ]; then echo "File exists" if [ -r "$file" ]; then echo "File is readable" if [ -s "$file" ]; then echo "File is not empty" else echo "File is empty" fi else echo "File is not readable" fi else echo "File does not exist" fi # Practical example: User authentication simulation read -p "Username: " username if [ -n "$username" ]; then read -sp "Password: " password echo if [ ${#password} -ge 8 ]; then if [ "$username" = "admin" ] && [ "$password" = "secret123" ]; then echo "Admin login successful" elif [ "$username" = "user" ] && [ "$password" = "pass1234" ]; then echo "User login successful" else echo "Invalid credentials" fi else echo "Password too short" fi else echo "Username required" fi # Configuration validation validate_config() { local config_file="$1" if [ -f "$config_file" ]; then echo "Config file found" if [ -r "$config_file" ]; then echo "Config file readable" # Check required settings if grep -q "^server=" "$config_file"; then server=$(grep "^server=" "$config_file" | cut -d= -f2) if [ -n "$server" ]; then echo "Server: $server" if grep -q "^port=" "$config_file"; then port=$(grep "^port=" "$config_file" | cut -d= -f2) if [[ "$port" =~ ^[0-9]+$ ]] && [ "$port" -ge 1024 ] && [ "$port" -le 65535 ]; then echo "Valid port: $port" return 0 else echo "Invalid port number" fi else echo "Missing port setting" fi else echo "Empty server setting" fi else echo "Missing server setting" fi else echo "Config file not readable" fi else echo "Config file not found" fi return 1 } 4. Conditional Command Execution
Using && and ||
#!/bin/bash # && executes next command only if previous succeeded mkdir newdir && cd newdir && touch file.txt echo "Directory created and file created" # || executes next command only if previous failed command1 || command2 || echo "Both commands failed" # Combining && and || [ -f "$file" ] && echo "File exists" || echo "File not found" # Practical examples # Create directory if it doesn't exist [ -d "$dir" ] || mkdir -p "$dir" # Check and install package command -v git &>/dev/null || sudo apt-get install -y git # Conditional execution chain compile() { gcc -c program.c && \ gcc -o program program.o && \ echo "Compilation successful" || \ { echo "Compilation failed" >&2; exit 1; } } # Short-circuit evaluation debug && echo "Debug mode enabled" # Safe deletion [ -f "$file" ] && rm "$file" # Complex chain test -f "$file" && \ cp "$file" "$backup" && \ gzip "$backup" && \ echo "Backup created" || \ echo "Backup failed" Ternary-like Expressions
#!/bin/bash # Using && and || as ternary operator result=$([ "$a" -gt "$b" ] && echo "a" || echo "b") # Using arithmetic ternary max=$(( a > b ? a : b )) # Function-like ternary ternary() { if eval "$1"; then echo "$2" else echo "$3" fi } status=$(ternary "[ -f '$file' ]" "exists" "missing") # Practical examples # Set variable based on condition log_level=$([ "$debug" = true ] && echo "DEBUG" || echo "INFO") # Command with default editor=${VISUAL:-${EDITOR:-vi}} # Path with fallback config_file=${1:-/etc/default/config} # Examples is_root=$([ "$EUID" -eq 0 ] && echo "yes" || echo "no") echo "Running as root: $is_root" # File type indicator file_type=$([ -f "$path" ] && echo "file" || ([ -d "$path" ] && echo "dir" || echo "other")) 5. Common Patterns and Use Cases
Input Validation
#!/bin/bash # Validate required arguments if [ $# -eq 0 ]; then echo "Error: No arguments provided" echo "Usage: $0 <filename>" exit 1 fi # Validate file input validate_file() { local file="$1" if [ -z "$file" ]; then echo "Error: No filename provided" return 1 fi if [ ! -e "$file" ]; then echo "Error: File does not exist: $file" return 1 fi if [ ! -f "$file" ]; then echo "Error: Not a regular file: $file" return 1 fi if [ ! -r "$file" ]; then echo "Error: File not readable: $file" return 1 fi return 0 } # Validate numeric input validate_number() { local num="$1" local min="${2:-}" local max="${3:-}" # Check if it's a number if ! [[ "$num" =~ ^-?[0-9]+$ ]]; then echo "Error: Not a valid number: $num" return 1 fi # Check minimum if [ -n "$min" ] && [ "$num" -lt "$min" ]; then echo "Error: Number must be >= $min" return 1 fi # Check maximum if [ -n "$max" ] && [ "$num" -gt "$max" ]; then echo "Error: Number must be <= $max" return 1 fi return 0 } # Validate email validate_email() { local email="$1" if [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then return 0 else echo "Error: Invalid email format: $email" return 1 fi } # Menu validation show_menu() { echo "1. Option 1" echo "2. Option 2" echo "3. Option 3" echo "4. Quit" } while true; do show_menu read -p "Choose option [1-4]: " choice case "$choice" in 1) echo "Option 1 selected" ;; 2) echo "Option 2 selected" ;; 3) echo "Option 3 selected" ;; 4) echo "Goodbye"; exit 0 ;; *) echo "Invalid option, please try again" ;; esac done Configuration File Parsing
#!/bin/bash # Parse simple config file parse_config() { local config_file="$1" if [ ! -f "$config_file" ]; then echo "Error: Config file not found" return 1 fi while IFS='=' read -r key value; do # Skip comments and empty lines if [[ "$key" =~ ^[[:space:]]*# ]] || [ -z "$key" ]; then continue fi # Trim whitespace key=$(echo "$key" | xargs) value=$(echo "$value" | xargs) # Validate key if [ -z "$key" ]; then echo "Warning: Invalid line: $key=$value" continue fi # Set variable based on key case "$key" in server) if [ -n "$value" ]; then SERVER="$value" else echo "Warning: server value empty" fi ;; port) if [[ "$value" =~ ^[0-9]+$ ]] && [ "$value" -ge 1 ] && [ "$value" -le 65535 ]; then PORT="$value" else echo "Warning: Invalid port: $value" fi ;; debug) if [[ "$value" =~ ^(yes|true|1)$ ]]; then DEBUG=true elif [[ "$value" =~ ^(no|false|0)$ ]]; then DEBUG=false else echo "Warning: Invalid debug value: $value" fi ;; *) echo "Warning: Unknown config key: $key" ;; esac done < "$config_file" } # Example usage CONFIG_FILE="app.conf" if [ -f "$CONFIG_FILE" ]; then parse_config "$CONFIG_FILE" # Check required settings if [ -z "$SERVER" ]; then echo "Error: server not configured" exit 1 fi if [ -z "$PORT" ]; then echo "Warning: port not set, using default 8080" PORT=8080 fi echo "Configuration loaded:" echo " Server: $SERVER" echo " Port: $PORT" echo " Debug: ${DEBUG:-false}" else echo "Using default configuration" SERVER="localhost" PORT=8080 DEBUG=false fi Error Handling Functions
#!/bin/bash # Comprehensive error handling set -euo pipefail # Error function error_exit() { echo "Error: $1" >&2 exit "${2:-1}" } # Warning function warning() { echo "Warning: $1" >&2 } # Info function info() { echo "Info: $1" } # Check prerequisites check_prerequisites() { local missing=() for cmd in "$@"; do if ! command -v "$cmd" &>/dev/null; then missing+=("$cmd") fi done if [ ${#missing[@]} -gt 0 ]; then error_exit "Missing required commands: ${missing[*]}" fi } # Check file with specific permissions check_file() { local file="$1" local required_perms="${2:-r}" if [ ! -e "$file" ]; then error_exit "File not found: $file" fi if [ ! -f "$file" ]; then error_exit "Not a regular file: $file" fi if [[ "$required_perms" == *"r"* ]] && [ ! -r "$file" ]; then error_exit "File not readable: $file" fi if [[ "$required_perms" == *"w"* ]] && [ ! -w "$file" ]; then error_exit "File not writable: $file" fi if [[ "$required_perms" == *"x"* ]] && [ ! -x "$file" ]; then error_exit "File not executable: $file" fi return 0 } # Check directory check_dir() { local dir="$1" local create="${2:-false}" if [ ! -d "$dir" ]; then if [ "$create" = true ]; then mkdir -p "$dir" || error_exit "Cannot create directory: $dir" info "Created directory: $dir" else error_exit "Directory not found: $dir" fi fi if [ ! -w "$dir" ]; then error_exit "Directory not writable: $dir" fi return 0 } # Check disk space check_disk_space() { local path="$1" local required_mb="${2:-100}" local available_kb=$(df "$path" | awk 'NR==2 {print $4}') local available_mb=$((available_kb / 1024)) if [ "$available_mb" -lt "$required_mb" ]; then error_exit "Insufficient disk space: ${available_mb}MB available, ${required_mb}MB required" fi info "Disk space OK: ${available_mb}MB available" } # Usage example main() { info "Starting script..." # Check prerequisites check_prerequisites "curl" "grep" "sed" # Check configuration file check_file "/etc/app/config.conf" "r" # Check log directory check_dir "/var/log/app" true # Check disk space check_disk_space "/var/log" 100 info "All checks passed, continuing..." } # Run main with error handling main "$@" || error_exit "Script failed" 6. Practical Examples
System Administration Script
#!/bin/bash # system_check.sh - Comprehensive system check set -euo pipefail # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' # Helper functions print_status() { local status="$1" local message="$2" case "$status" in OK) echo -e "${GREEN}[OK]${NC} $message" ;; WARN) echo -e "${YELLOW}[WARN]${NC} $message" ;; ERROR) echo -e "${RED}[ERROR]${NC} $message" ;; INFO) echo -e "${BLUE}[INFO]${NC} $message" ;; esac } # Check CPU load check_cpu() { local load=$(uptime | awk -F'load average:' '{print $2}' | cut -d, -f1 | xargs) local cores=$(nproc) local threshold=$(echo "$cores * 0.7" | bc) if (( $(echo "$load > $threshold" | bc -l) )); then print_status "WARN" "CPU load high: $load (threshold: $threshold)" else print_status "OK" "CPU load normal: $load" fi } # Check memory usage check_memory() { local total=$(free -m | awk 'NR==2{print $2}') local used=$(free -m | awk 'NR==2{print $3}') local percent=$((used * 100 / total)) if [ "$percent" -gt 90 ]; then print_status "ERROR" "Memory critical: ${used}MB/${total}MB (${percent}%)" elif [ "$percent" -gt 80 ]; then print_status "WARN" "Memory high: ${used}MB/${total}MB (${percent}%)" else print_status "OK" "Memory normal: ${used}MB/${total}MB (${percent}%)" fi } # Check disk usage check_disk() { local threshold=80 df -h | grep -E '^/dev/' | while read line; do local filesystem=$(echo "$line" | awk '{print $1}') local usage=$(echo "$line" | awk '{print $5}' | sed 's/%//') local mount=$(echo "$line" | awk '{print $6}') if [ "$usage" -gt "$threshold" ]; then print_status "WARN" "Disk $filesystem ($mount) at ${usage}%" else print_status "OK" "Disk $filesystem ($mount) at ${usage}%" fi done } # Check running services check_services() { local services=("ssh" "cron" "rsyslog") for service in "${services[@]}"; do if systemctl is-active --quiet "$service"; then print_status "OK" "Service $service is running" else print_status "ERROR" "Service $service is not running" fi done } # Check network connectivity check_network() { local hosts=("8.8.8.8" "1.1.1.1") for host in "${hosts[@]}"; do if ping -c 1 -W 2 "$host" &>/dev/null; then print_status "OK" "Network connectivity to $host" else print_status "WARN" "Cannot reach $host" fi done } # Check recent errors in logs check_logs() { local logfile="/var/log/syslog" local errors=$(grep -i "error" "$logfile" 2>/dev/null | tail -5) if [ -n "$errors" ]; then print_status "WARN" "Recent errors found in logs:" echo "$errors" | sed 's/^/ /' else print_status "OK" "No recent errors in logs" fi } # Main function main() { print_status "INFO" "Starting system check at $(date)" echo "----------------------------------------" check_cpu check_memory check_disk check_services check_network check_logs echo "----------------------------------------" print_status "INFO" "System check completed at $(date)" } # Run main main User Input Validation Script
#!/bin/bash # input_validation.sh - Comprehensive input validation set -euo pipefail # Color codes RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # Validation functions validate_required() { local value="$1" local field="$2" if [ -z "$value" ]; then echo -e "${RED}Error: $field is required${NC}" return 1 fi return 0 } validate_length() { local value="$1" local min="$2" local max="$3" local field="$4" local len=${#value} if [ "$len" -lt "$min" ]; then echo -e "${RED}Error: $field must be at least $min characters${NC}" return 1 fi if [ "$len" -gt "$max" ]; then echo -e "${RED}Error: $field must not exceed $max characters${NC}" return 1 fi return 0 } validate_pattern() { local value="$1" local pattern="$2" local field="$3" if [[ ! "$value" =~ $pattern ]]; then echo -e "${RED}Error: $field has invalid format${NC}" return 1 fi return 0 } validate_range() { local value="$1" local min="$2" local max="$3" local field="$4" if [ "$value" -lt "$min" ] || [ "$value" -gt "$max" ]; then echo -e "${RED}Error: $field must be between $min and $max${NC}" return 1 fi return 0 } validate_email() { local email="$1" local pattern="^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$" if [[ ! "$email" =~ $pattern ]]; then echo -e "${RED}Error: Invalid email address${NC}" return 1 fi return 0 } validate_phone() { local phone="$1" # Remove common formatting characters phone=$(echo "$phone" | tr -d '[-.() ]') if [[ ! "$phone" =~ ^[0-9]{10,15}$ ]]; then echo -e "${RED}Error: Invalid phone number (must be 10-15 digits)${NC}" return 1 fi return 0 } validate_date() { local date="$1" local format="$2" case "$format" in YYYY-MM-DD) if [[ ! "$date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then echo -e "${RED}Error: Date must be in YYYY-MM-DD format${NC}" return 1 fi ;; MM/DD/YYYY) if [[ ! "$date" =~ ^[0-9]{2}/[0-9]{2}/[0-9]{4}$ ]]; then echo -e "${RED}Error: Date must be in MM/DD/YYYY format${NC}" return 1 fi ;; *) return 1 ;; esac return 0 } validate_password() { local password="$1" local errors=() # Length check if [ ${#password} -lt 8 ]; then errors+=("at least 8 characters") fi # Uppercase check if ! [[ "$password" =~ [A-Z] ]]; then errors+=("an uppercase letter") fi # Lowercase check if ! [[ "$password" =~ [a-z] ]]; then errors+=("a lowercase letter") fi # Digit check if ! [[ "$password" =~ [0-9] ]]; then errors+=("a digit") fi # Special character check if ! [[ "$password" =~ [[:punct:]] ]]; then errors+=("a special character") fi if [ ${#errors[@]} -gt 0 ]; then echo -e "${RED}Error: Password must contain:${NC}" printf " - %s\n" "${errors[@]}" return 1 fi return 0 } # Main registration form echo "=== User Registration ===" echo errors=() # Get and validate username while true; do read -p "Username: " username if validate_required "$username" "Username" && \ validate_length "$username" 3 20 "Username" && \ validate_pattern "$username" '^[a-zA-Z0-9_]+$' "Username (alphanumeric and underscore only)"; then break fi echo done # Get and validate email while true; do read -p "Email: " email if validate_required "$email" "Email" && \ validate_email "$email"; then break fi echo done # Get and validate phone while true; do read -p "Phone: " phone if validate_required "$phone" "Phone" && \ validate_phone "$phone"; then break fi echo done # Get and validate age while true; do read -p "Age: " age if validate_required "$age" "Age" && \ validate_pattern "$age" '^[0-9]+$' "Age (numbers only)" && \ validate_range "$age" 18 120 "Age"; then break fi echo done # Get and validate password while true; do read -sp "Password: " password echo read -sp "Confirm password: " password2 echo if [ "$password" != "$password2" ]; then echo -e "${RED}Error: Passwords do not match${NC}" elif validate_password "$password"; then break fi echo done # Success message echo -e "\n${GREEN}✓ Registration successful!${NC}" echo "Username: $username" echo "Email: $email" echo "Phone: $phone" echo "Age: $age" File Processing Script
#!/bin/bash # file_processor.sh - Conditional file processing set -euo pipefail # Configuration INPUT_DIR="${1:-./input}" OUTPUT_DIR="${2:-./output}" PROCESSED_DIR="${3:-./processed}" LOG_FILE="processor.log" # Create directories if they don't exist for dir in "$INPUT_DIR" "$OUTPUT_DIR" "$PROCESSED_DIR"; do if [ ! -d "$dir" ]; then mkdir -p "$dir" echo "Created directory: $dir" fi done # Logging function log() { local level="$1" local message="$2" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE" } # Check if input directory has files check_input_files() { if [ ! -d "$INPUT_DIR" ]; then log "ERROR" "Input directory does not exist: $INPUT_DIR" return 1 fi local file_count=$(find "$INPUT_DIR" -maxdepth 1 -type f | wc -l) if [ "$file_count" -eq 0 ]; then log "WARNING" "No files found in input directory" return 1 fi log "INFO" "Found $file_count files to process" return 0 } # Process a single file process_file() { local file="$1" local filename=$(basename "$file") local extension="${filename##*.}" local output_file="$OUTPUT_DIR/$filename" log "INFO" "Processing: $filename" # Check file permissions if [ ! -r "$file" ]; then log "ERROR" "File not readable: $filename" return 1 fi # Check file size local size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file" 2>/dev/null) if [ "$size" -eq 0 ]; then log "WARNING" "File is empty: $filename" # Optionally handle empty files cp "$file" "$output_file.empty" return 0 fi # Process based on file type case "${extension,,}" in txt|text) log "INFO" "Processing text file: $filename" # Convert to uppercase tr '[:lower:]' '[:upper:]' < "$file" > "$output_file" ;; csv) log "INFO" "Processing CSV file: $filename" # Add line numbers awk '{print NR "," $0}' "$file" > "$output_file" ;; log) log "INFO" "Processing log file: $filename" # Extract errors grep -i "error" "$file" > "$output_file.errors" 2>/dev/null || true if [ ! -s "$output_file.errors" ]; then rm -f "$output_file.errors" log "INFO" "No errors found in $filename" fi # Also keep original with timestamp cp "$file" "$output_file" ;; sh) log "INFO" "Processing shell script: $filename" # Check if executable if [ -x "$file" ]; then cp "$file" "$output_file" chmod +x "$output_file" else log "WARNING" "Script not executable: $filename" cp "$file" "$output_file" fi ;; *) log "INFO" "Unknown file type: $extension - copying as-is" cp "$file" "$output_file" ;; esac # Check if processing succeeded if [ $? -eq 0 ] && [ -f "$output_file" ]; then log "INFO" "Successfully processed: $filename" # Move original to processed directory mv "$file" "$PROCESSED_DIR/$filename" return 0 else log "ERROR" "Failed to process: $filename" return 1 fi } # Generate report generate_report() { local report_file="$OUTPUT_DIR/processing_report.txt" { echo "=== Processing Report ===" echo "Date: $(date)" echo "Input directory: $INPUT_DIR" echo "Output directory: $OUTPUT_DIR" echo "Processed directory: $PROCESSED_DIR" echo "=========================" echo echo "Processed files:" find "$PROCESSED_DIR" -type f -printf "%f\n" 2>/dev/null | sort | sed 's/^/ - /' echo echo "Output files:" find "$OUTPUT_DIR" -type f -printf "%f (%s bytes)\n" 2>/dev/null | sort | sed 's/^/ - /' echo echo "Processing log:" tail -20 "$LOG_FILE" 2>/dev/null | sed 's/^/ /' } > "$report_file" log "INFO" "Report generated: $report_file" } # Main execution main() { log "INFO" "=== File Processor Started ===" if check_input_files; then local success_count=0 local fail_count=0 # Process each file while IFS= read -r -d '' file; do if process_file "$file"; then ((success_count++)) else ((fail_count++)) fi done < <(find "$INPUT_DIR" -maxdepth 1 -type f -print0) log "INFO" "Processing complete: $success_count succeeded, $fail_count failed" # Generate report generate_report else log "WARNING" "No files to process" fi log "INFO" "=== File Processor Finished ===" } # Run main main 7. Best Practices and Tips
If Statement Best Practices
#!/bin/bash # 1. Always quote variables in [ ] # Good if [ "$name" = "John" ]; then echo "Hello John" fi # Bad - breaks if name is empty or has spaces if [ $name = "John" ]; then echo "Hello John" fi # 2. Use [[ ]] for bash scripts # Better - more features, no word splitting if [[ "$name" == "John" && -f "$file" ]]; then echo "John's file exists" fi # 3. Use (( )) for arithmetic # Cleaner if (( age >= 18 )); then echo "Adult" fi # 4. Use meaningful condition names # Good is_root() { [ "$EUID" -eq 0 ] } if is_root; then echo "Running as root" fi # 5. Keep conditions simple # Instead of complex conditions if [[ "$file" == *.txt ]] && [ -f "$file" ] && [ -r "$file" ] && [ -s "$file" ]; then # process file fi # Create helper functions is_valid_text_file() { [[ "$1" == *.txt ]] && [ -f "$1" ] && [ -r "$1" ] && [ -s "$1" ] } if is_valid_text_file "$file"; then # process file fi # 6. Use case statements for multiple conditions # Instead of multiple if-elif case "$action" in start|stop|restart|status) service "$action" "$service_name" ;; *) echo "Unknown action: $action" ;; esac # 7. Handle errors early # Guard pattern [ -f "$file" ] || { echo "File not found"; exit 1; } [ -r "$file" ] || { echo "File not readable"; exit 1; } # Continue with main logic # 8. Use && and || for simple conditions [ -d "$dir" ] || mkdir -p "$dir" [ -f "$file" ] && rm "$file" # 9. Return meaningful exit codes check_file() { if [ ! -f "$1" ]; then return 1 # File not found fi if [ ! -r "$1" ]; then return 2 # File not readable fi return 0 # Success } if check_file "$file"; then echo "File OK" else case $? in 1) echo "File not found" ;; 2) echo "File not readable" ;; esac fi # 10. Document complex conditions # Check if user has required permissions and is in correct group # and file exists and is within size limits if [[ "$USER" == "$owner" ]] || [[ " ${groups[*]} " =~ " $required_group " ]]; then if [ -f "$file" ] && [ -r "$file" ] && [ $(stat -c%s "$file") -lt $MAX_SIZE ]; then # process file fi fi Common Pitfalls and Solutions
#!/bin/bash # Pitfall 1: Spaces in variable names filename="my file.txt" # Wrong if [ -f $filename ]; then # Expands to [ -f my file.txt ] -> error echo "File exists" fi # Correct if [ -f "$filename" ]; then echo "File exists" fi # Pitfall 2: Empty variables unset var # Wrong if [ $var = "value" ]; then # Expands to [ = "value" ] -> error echo "Match" fi # Correct if [ "$var" = "value" ]; then # Expands to [ "" = "value" ] -> false echo "Match" fi # Better if [[ $var == "value" ]]; then # No quotes needed echo "Match" fi # Pitfall 3: Integer vs string comparison num=10 # Wrong if [ "$num" > 5 ]; then # String comparison, not numeric echo "Greater" fi # Correct if [ "$num" -gt 5 ]; then echo "Greater" fi # Better if (( num > 5 )); then echo "Greater" fi # Pitfall 4: Using -a and -o (deprecated) # Wrong if [ -f "$file" -a -r "$file" ]; then echo "Readable file" fi # Correct if [ -f "$file" ] && [ -r "$file" ]; then echo "Readable file" fi # Pitfall 5: Forgetting semicolon before then # Wrong if [ -f "$file" ] then echo "File exists" fi # Correct if [ -f "$file" ]; then echo "File exists" fi # Pitfall 6: Using = for numeric comparison # Wrong if [ "$a" = "$b" ]; then # String comparison echo "Equal" fi # Correct for numbers if [ "$a" -eq "$b" ]; then echo "Equal" fi # Pitfall 7: Not handling command failures # Wrong if cp file1 file2; then echo "Copy succeeded" fi # cp might fail, but we don't check # Correct if cp file1 file2 2>/dev/null; then echo "Copy succeeded" else echo "Copy failed" >&2 exit 1 fi # Pitfall 8: Using test command with unquoted wildcards # Wrong if [ -f *.txt ]; then # Expands to multiple arguments echo "Text file exists" fi # Correct if ls *.txt &>/dev/null; then echo "Text files exist" fi # Or if find . -maxdepth 1 -name "*.txt" -print -quit | grep -q .; then echo "Text files exist" fi
Performance Considerations
#!/bin/bash # 1. Use shell builtins instead of external commands # Slow if [ "$(echo "$string" | wc -c)" -gt 10 ]; then echo "Long string" fi # Fast if [ "${#string}" -gt 10 ]; then echo "Long string" fi # 2. Avoid unnecessary subshells # Slow if grep -q "pattern" file.txt; then count=$(grep -c "pattern" file.txt) echo "Found $count matches" fi # Fast count=$(grep -c "pattern" file.txt) if [ "$count" -gt 0 ]; then echo "Found $count matches" fi # 3. Use && and || for simple conditions # Instead of if [ -d "$dir" ]; then : # do nothing else mkdir "$dir" fi # Better [ -d "$dir" ] || mkdir "$dir" # 4. Use case instead of multiple if statements # Slow if [ "$var" = "a" ]; then process_a elif [ "$var" = "b" ]; then process_b elif [ "$var" = "c" ]; then process_c fi # Fast case "$var" in a) process_a ;; b) process_b ;; c) process_c ;; esac # 5. Group conditions to avoid multiple evaluations # Slow if [ -f "$file" ]; then if [ -r "$file" ]; then if [ -s "$file" ]; then process_file "$file" fi fi fi # Fast if [ -f "$file" ] && [ -r "$file" ] && [ -s "$file" ]; then process_file "$file" fi # 6. Use early returns to avoid deep nesting # Bad process() { if [ -f "$file" ]; then if [ -r "$file" ]; then # process fi fi } # Good process() { [ -f "$file" ] || return 1 [ -r "$file" ] || return 2 # process return 0 } Conclusion
The if...else statement is a fundamental building block of Bash scripting:
Key Takeaways
- Test Conditions: Use
[ ]for POSIX compliance,[[ ]]for bash-specific features - File Tests: Check file existence, type, and permissions
- String Tests: Compare and check strings
- Numeric Tests: Use
-eq,-ne,-lt, etc., or(( ))for arithmetic - Logical Operators: Combine conditions with
&&,||, and! - Nested Conditions: Handle complex decision trees
- Error Handling: Always check return values and handle errors
Common Patterns Cheat Sheet
| Pattern | Purpose | Example |
|---|---|---|
if [ -f "$file" ] | Check file exists | if [ -f "/etc/passwd" ] |
if [ -z "$var" ] | Check if variable is empty | if [ -z "$name" ] |
if [ "$a" -eq "$b" ] | Numeric equality | if [ "$age" -eq 18 ] |
if [[ "$str" == *pattern* ]] | Pattern matching | if [[ "$file" == *.txt ]] |
if (( a > b )) | Arithmetic comparison | if (( score >= 60 )) |
if command; then | Command success check | if grep -q "error" log; then |
[ -d "$dir" ] || mkdir "$dir" | Create if not exists | [ -d "$dir" ] || mkdir -p "$dir" |
[ -f "$file" ] && rm "$file" | Remove if exists | [ -f "$temp" ] && rm "$temp" |
Best Practices Summary
- Always quote variables in
[ ]tests - Use
[[ ]]for bash scripts (more features, safer) - Use
(( ))for arithmetic comparisons - Keep conditions simple and use helper functions
- Handle errors early with guard clauses
- Use meaningful variable names for conditions
- Document complex logic with comments
- Test edge cases (empty variables, missing files, etc.)
- Return meaningful exit codes from functions
- Use case statements for multiple condition checks
Mastering conditional statements is essential for writing robust and maintainable Bash scripts. Practice with different scenarios to become proficient in choosing the right condition for each situation.
Complete Bash Exercises with Solutions
Practice Bash scripting with solved exercises to improve your command-line and scripting skills.
Link: https://macronepal.com/complete-bash-exercises-with-solutions/
Complete Guide to Bash Crontab Command
Learn how to schedule and automate tasks efficiently using the Bash crontab command.
Link: https://macronepal.com/complete-guide-to-bash-crontab-command-schedule-tasks/
Complete Guide to Bash Arrays
Understand how to create, manage, and use arrays in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-arrays/
Bash Quiz: Test Your Knowledge
Test your Bash scripting knowledge with quizzes and practical questions.
Link: https://macronepal.com/bash-quiz-test-your-knowledge/
Complete Guide to Bash Functions
Learn how to write reusable Bash functions for cleaner and efficient scripts.
Link: https://macronepal.com/complete-guide-to-bash-functions/
Complete Guide to Bash Loops
Master loops in Bash for repetitive tasks and automation.
Link: https://macronepal.com/complete-guide-to-bash-loops/
Complete Guide to Bash If-Else Statements
Learn decision-making in Bash using conditional statements.
Link: https://macronepal.com/complete-guide-to-bash-ifelse-statements/
Complete Guide to Bash Operators
Explore arithmetic, logical, and comparison operators in Bash.
Link: https://macronepal.com/complete-guide-to-bash-operators/
Complete Guide to Bash Data Types
Understand variables and different data types used in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-data-types/
Complete Guide to Bash Variables
Learn how to declare, assign, and use variables in Bash scripts.
Link: https://macronepal.com/complete-guide-to-bash-variables/
Complete Guide to Bash Scripting
A complete introduction to writing Bash scripts for automation.
Link: https://macronepal.com/complete-guide-to-bash-scripting/
Complete Guide to Basic Bash Syntax
Learn the fundamental syntax needed to write Bash commands and scripts.
Link: https://macronepal.com/complete-guide-to-basic-bash-syntax/
Complete Guide to Bash Chgrp Command
Learn how to change group ownership of files and directories.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership-2/
Complete Guide to Bash Chgrp Command (Alternate)
Another detailed explanation of Bash group ownership management.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership/
Complete Guide to Bash File Permissions and Ownership
Understand Linux file permissions and ownership management.
Link: https://macronepal.com/complete-guide-to-bash-file-permissions-and-ownership/
Complete Guide to Bash Chmod Command
Learn how to modify file permissions using chmod.
Link: https://macronepal.com/complete-guide-to-bash-chmod-command-change-file-permissions/
Complete Guide to Bash Chown Command
Change file ownership using the Bash chown command.
Link: https://macronepal.com/complete-guide-to-bash-chown-command-change-file-ownership/
Complete Guide to Bash SCP Command
Securely transfer files between systems using SCP.
Link: https://macronepal.com/complete-guide-to-bash-scp-command-secure-copy/
Complete Guide to Bash SSH Command
Learn secure remote access and server management using SSH.
Link: https://macronepal.com/complete-guide-to-bash-ssh-command/
Bash Wget Command Complete Guide
Download files from the internet using the wget command.
Link: https://macronepal.com/bash-wget-command-complete-guide-to-non-interactive-network-downloader/
