Table of Contents
- Understanding Shell Scripting Basics
- Real-World Applications and Use Cases
- Common Practices for Effective Scripting
- Best Practices for Production-Ready Scripts
- Conclusion
- References
Understanding Shell Scripting Basics
Before diving into real-world use cases, let’s establish foundational concepts.
What is a Shell Script?
A shell script is a text file containing a series of commands that the shell (e.g., Bash, Zsh) executes sequentially. It eliminates the need to run commands manually, enabling automation of tasks like file manipulation, system monitoring, and application deployment.
Common Shells
The choice of shell impacts script behavior. Popular shells include:
- Bash (Bourne-Again Shell): The most widely used shell, default on Linux and macOS.
- Zsh: Extends Bash with features like auto-completion and themes (popular in developer workflows).
- Sh (Bourne Shell): Legacy shell, often used for minimal environments (e.g., embedded systems).
We’ll focus on Bash in examples, as it’s the de facto standard for cross-platform scripting.
Core Syntax Fundamentals
Shebang Line
Every script should start with a “shebang” (#!) to specify the interpreter:
#!/bin/bash
# This script uses Bash
Variables
Store and manipulate data with variables (no spaces around =):
NAME="Alice"
echo "Hello, $NAME!" # Output: Hello, Alice!
Loops and Conditionals
Control flow is critical for automation:
# For loop example
for i in {1..3}; do
echo "Iteration $i"
done
# Conditional example
FILE="data.txt"
if [ -f "$FILE" ]; then
echo "$FILE exists."
else
echo "$FILE not found."
fi
Real-World Applications and Use Cases
Shell scripts shine in solving practical problems. Below are key use cases with actionable examples.
1. System Administration
System admins rely on shell scripts to manage infrastructure, automate maintenance, and ensure system health.
Example 1: Automated Log Rotation
Logs grow indefinitely, consuming disk space. A script to compress old logs and delete outdated ones:
#!/bin/bash
# log_rotator.sh: Compress logs older than 7 days and delete >30 days old
LOG_DIR="/var/log/myapp"
MAX_AGE_COMPRESS=7 # Days
MAX_AGE_DELETE=30 # Days
# Compress logs older than MAX_AGE_COMPRESS days
find "$LOG_DIR" -name "*.log" -type f -mtime +"$MAX_AGE_COMPRESS" -exec gzip {} \;
# Delete compressed logs older than MAX_AGE_DELETE days
find "$LOG_DIR" -name "*.log.gz" -type f -mtime +"$MAX_AGE_DELETE" -delete
echo "Log rotation completed at $(date)" >> "$LOG_DIR/rotation.log"
Why it works: Uses find to target files by age, gzip for compression, and logs activity for auditing.
Example 2: Backup Automation with Error Handling
Automate backups to a remote server (e.g., via rsync), with checks for failures:
#!/bin/bash
# backup_data.sh: Sync local data to remote server with error handling
SOURCE="/home/user/documents"
DEST="backup-server:/backups/user"
LOG_FILE="/var/log/backup.log"
# Exit on error, unset variables, or failed pipes
set -euo pipefail
# Log start time
echo "Backup started at $(date)" >> "$LOG_FILE"
# Perform backup with rsync (verbose, archive mode)
rsync -av --delete "$SOURCE" "$DEST" >> "$LOG_FILE" 2>&1
# Log success
echo "Backup completed successfully at $(date)" >> "$LOG_FILE"
Key practices: set -euo pipefail ensures the script exits on errors/unset variables, and 2>&1 redirects errors to the log.
2. DevOps and CI/CD
Shell scripts integrate seamlessly into CI/CD pipelines, automating testing, deployment, and infrastructure provisioning.
Example 3: CI/CD Pipeline for a Python App
A script to run tests, build, and deploy a Python application if tests pass:
#!/bin/bash
# deploy_app.sh: CI/CD workflow for a Python app
APP_DIR="/opt/my-python-app"
TEST_REPORT="test_results.txt"
# Step 1: Run unit tests
echo "Running tests..."
pytest "$APP_DIR/tests" > "$TEST_REPORT" 2>&1
# Step 2: Check test results (exit code 0 = success)
if [ $? -ne 0 ]; then
echo "Tests failed! See $TEST_REPORT"
exit 1
fi
# Step 3: Build and deploy
echo "Building app..."
python "$APP_DIR/setup.py" build >> "$TEST_REPORT" 2>&1
echo "Deploying to production..."
systemctl restart my-app-service
echo "Deployment successful at $(date)"
Use case: Trigger this script in GitHub Actions or GitLab CI to automate deployments on code pushes.
3. Data Processing and Log Analysis
Shell scripts are ideal for parsing logs, transforming data, and generating reports—no need for Python/R for simple tasks.
Example 4: Extract Errors from Logs and Alert
Parse a web server log to find 5xx errors and send an email alert:
#!/bin/bash
# error_alert.sh: Detect 5xx errors and alert via email
LOG_FILE="/var/log/nginx/access.log"
ERROR_THRESHOLD=5 # Alert if >5 errors in 1 hour
ALERT_EMAIL="[email protected]"
# Extract 5xx errors in the last hour
ERRORS=$(grep " $(date -d '1 hour ago' +%H):" "$LOG_FILE" | grep -c " 5[0-9][0-9] ")
if [ "$ERRORS" -gt "$ERROR_THRESHOLD" ]; then
SUBJECT="ALERT: High 5xx Errors on Web Server"
BODY="Found $ERRORS 5xx errors in the last hour. Check $LOG_FILE."
echo "$BODY" | mail -s "$SUBJECT" "$ALERT_EMAIL"
echo "Alert sent to $ALERT_EMAIL"
else
echo "No critical errors detected."
fi
Tools used: grep for filtering, date for time-based parsing, and mail for alerts.
Example 5: CSV Data Filtering
Filter a CSV file to extract rows where a column meets a condition (e.g., “sales > 1000”):
#!/bin/bash
# filter_sales.sh: Extract high-value sales from a CSV
INPUT_CSV="sales_data.csv"
OUTPUT_CSV="high_sales.csv"
MIN_SALES=1000
# CSV format: date,product,sales (header row)
# Use awk to filter rows where sales > MIN_SALES
awk -v min="$MIN_SALES" 'BEGIN {FS=","} NR==1; NR>1 && $3 > min' "$INPUT_CSV" > "$OUTPUT_CSV"
echo "Filtered $INPUT_CSV to $OUTPUT_CSV (sales > $MIN_SALES)"
Why it works: awk efficiently processes structured data; NR==1 preserves the header.
4. Automation Workflows
Shell scripts automate end-to-end workflows, from file cleanup to cloud resource management.
Example 6: Scheduled Task with Cron
Use cron to run scripts at specific intervals. For example, schedule the backup_data.sh script to run daily at 2 AM:
- Open the crontab editor:
crontab -e - Add:
0 2 * * * /path/to/backup_data.sh # Run daily at 2:00 AM
Example 7: Cloud Resource Cleanup (AWS)
Automatically terminate unused EC2 instances tagged “temporary” older than 7 days:
#!/bin/bash
# aws_cleanup.sh: Terminate old temporary EC2 instances
# Get instance IDs older than 7 days with tag "temporary=true"
INSTANCE_IDS=$(aws ec2 describe-instances \
--filters "Name=tag:temporary,Values=true" \
--query "Reservations[].Instances[?LaunchTime<='$(date -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)'].InstanceId" \
--output text)
# Terminate instances if any exist
if [ -n "$INSTANCE_IDS" ]; then
echo "Terminating instances: $INSTANCE_IDS"
aws ec2 terminate-instances --instance-ids "$INSTANCE_IDS"
else
echo "No temporary instances older than 7 days found."
fi
Use case: Run this weekly to reduce cloud costs by cleaning up forgotten resources.
Common Practices for Effective Scripting
To write maintainable and reliable scripts, follow these practices:
Modularity with Functions
Break scripts into reusable functions to avoid repetition:
#!/bin/bash
# Reusable logging function
log() {
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
echo "[$timestamp] $1" >> "$LOG_FILE"
}
# Usage
LOG_FILE="app.log"
log "Starting backup..."
log "Backup failed!" # Logs with timestamp
Error Handling
set -euo pipefail: Exit on errors (-e), unset variables (-u), and failed pipes (-o pipefail).- Trap signals: Clean up temporary files on script exit (success or failure):
TEMP_FILE=$(mktemp) trap 'rm -f "$TEMP_FILE"' EXIT # Delete TEMP_FILE on exit
Logging
Always log actions and errors for debugging:
# Log to file and stdout
log() {
echo "[$(date)] $*" | tee -a "script.log" # tee writes to both stdout and file
}
Input Validation
Check for required arguments or dependencies:
#!/bin/bash
# validate_input.sh
if [ $# -ne 1 ]; then
echo "Usage: $0 <input-file>"
exit 1
fi
INPUT_FILE="$1"
if [ ! -f "$INPUT_FILE" ]; then
echo "Error: $INPUT_FILE does not exist."
exit 1
fi
Best Practices for Production-Ready Scripts
To ensure scripts are secure, scalable, and easy to maintain:
- Use a Shebang: Always start with
#!/bin/bash(not#!/bin/shfor portability). - Avoid Hard-Coded Values: Use variables or command-line arguments instead:
# Bad: HARDCODED_DIR="/tmp" # Good: DIR="${1:-/tmp}" # Default to /tmp if no argument - Use Absolute Paths: Avoid relative paths to prevent confusion:
# Bad: cp data.txt ../backup # Depends on current directory # Good: cp "/home/user/data.txt" "/mnt/backup" - Test Thoroughly: Use
bash -n script.shto check syntax, and test edge cases (e.g., empty files, missing arguments). - Security:
- Avoid
eval(risk of command injection). - Sanitize user input:
read -r INPUT(-rprevents backslash escapes). - Restrict permissions:
chmod 700 script.sh(only owner can execute).
- Avoid
Conclusion
Shell scripting is a versatile tool for solving real-world automation challenges across system administration, DevOps, and data processing. By mastering core syntax, leveraging practical use cases (like log rotation, CI/CD, and cloud cleanup), and following best practices (modularity, error handling, security), you can write scripts that are efficient, maintainable, and scalable.
Whether you’re automating daily tasks or integrating into enterprise workflows, shell scripting remains an indispensable skill for modern tech professionals.
References
- Bash Official Documentation
- GNU Coreutils (Essential command-line tools)
- “The Linux Command Line” by William Shotts (free online: linuxcommand.org)
- “Bash Cookbook” by Carl Albing and JP Vossen (O’Reilly Media)
- AWS CLI Documentation