Bash Scripting

Bash scripts can make decisions, loop over data, and handle errors — turning simple commands into powerful automation.

Conditionals

Bash
# if/elif/else
if [ -f "config.txt" ]; then
    echo "Config file exists"
elif [ -d "config/" ]; then
    echo "Config directory exists"
else
    echo "No config found"
fi

# String comparison
if [ "$name" = "Alice" ]; then echo "Hello Alice"; fi
if [ -z "$var" ]; then echo "Variable is empty"; fi    # Zero length
if [ -n "$var" ]; then echo "Variable has value"; fi   # Non-zero length

# Numeric comparison
if [ $age -gt 18 ]; then echo "Adult"; fi      # Greater than
if [ $age -lt 18 ]; then echo "Minor"; fi      # Less than
if [ $age -ge 18 ]; then echo "18 or older"; fi # Greater or equal
if [ $count -eq 0 ]; then echo "Empty"; fi      # Equal
if [ $count -ne 0 ]; then echo "Not empty"; fi  # Not equal

# File tests
if [ -f "$file" ]; then echo "Is a file"; fi
if [ -d "$dir" ]; then echo "Is a directory"; fi
if [ -r "$file" ]; then echo "Is readable"; fi
if [ -w "$file" ]; then echo "Is writable"; fi
if [ -x "$script" ]; then echo "Is executable"; fi
if [ -s "$file" ]; then echo "File is not empty"; fi

# Double brackets (bash-specific, more powerful)
if [[ "$name" =~ ^[A-Z] ]]; then echo "Starts with uppercase"; fi
if [[ "$file" == *.log ]]; then echo "Is a log file"; fi

Loops

Bash
# for loop
for i in 1 2 3 4 5; do
    echo "Number: $i"
done

# C-style for loop
for ((i=0; i<10; i++)); do
    echo "Index: $i"
done

# Range
for i in {1..10}; do
    echo $i
done

# Loop over files
for file in *.txt; do
    echo "Processing $file..."
    wc -l "$file"
done

# Loop over array
colors=("red" "green" "blue")
for color in "${colors[@]}"; do
    echo "Color: $color"
done

# while loop
count=0
while [ $count -lt 5 ]; do
    echo "Count: $count"
    ((count++))
done

# Read file line by line
while IFS= read -r line; do
    echo "Line: $line"
done < file.txt

# until loop (runs until condition is true)
until [ -f "/tmp/ready" ]; do
    sleep 1
done
echo "File appeared!"

Functions

Bash
# Function definition
greet() {
    local name=$1    # local scope
    echo "Hello, $name!"
}

# Call it
greet "Alice"    # Hello, Alice!

# Return values
is_valid_email() {
    local email=$1
    if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
        return 0    # Success (true)
    else
        return 1    # Failure (false)
    fi
}

# Command substitution for output
get_file_count() {
    local dir=${1:-.}
    echo $(find "$dir" -type f | wc -l)
}

count=$(get_file_count "/var/log")
echo "Files: $count"

Error Handling

Bash
# Trap errors
set -e          # Exit on error
set -u          # Exit on undefined variable
set -o pipefail # Exit on pipe failure

# Trap for cleanup
cleanup() {
    echo "Cleaning up temp files..."
    rm -f /tmp/myapp_*
}
trap cleanup EXIT

# Error handling pattern
deploy() {
    local env=$1
    
    echo "Deploying to $env..."
    
    if ! build_app; then
        echo "ERROR: Build failed!" >&2
        return 1
    fi
    
    if ! push_to_server "$env"; then
        echo "ERROR: Push failed!" >&2
        return 1
    fi
    
    echo "Deployment complete!"
}

Practical Script: Backup

Bash
#!/bin/bash
set -euo pipefail

# Configuration
BACKUP_DIR="/backup"
SOURCE_DIR="/var/www"
DATE=$(date +%Y-%m-%d_%H-%M)
BACKUP_NAME="www_backup_${DATE}.tar.gz"
LOG_FILE="/var/log/backup.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

log "Starting backup of $SOURCE_DIR"

# Create backup
if tar -czf "${BACKUP_DIR}/${BACKUP_NAME}" -C "$(dirname $SOURCE_DIR)" "$(basename $SOURCE_DIR)"; then
    log "Backup created: ${BACKUP_NAME}"
else
    log "ERROR: Backup creation failed!"
    exit 1
fi

# Remove backups older than 30 days
find "$BACKUP_DIR" -name "www_backup_*.tar.gz" -mtime +30 -delete
log "Old backups cleaned up"

log "Backup complete!"

💡 Tip: Always add set -euo pipefail at the top of scripts. -e exits on error, -u catches undefined variables, and -o pipefail catches errors in pipes.

Next: Error Handling and Advanced

Knowledge Check

4 questions — test your understanding

1

What is the difference between `for i in $(seq 1 5)` and `for ((i=1; i<=5; i++))`?

2

What does `local` do when declaring a variable inside a function?

3

What does `trap cleanup EXIT` do?

4

What does `$((expression))` do in Bash?