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"; fiLoops
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 pipefailat the top of scripts.-eexits on error,-ucatches undefined variables, and-o pipefailcatches errors in pipes.
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?