Putting It All Together
You've learned the building blocks. Now it's time to combine them into real, useful scripts. This lesson walks through three complete projects and covers best practices for production scripts.
Project 1: System Information Script
This script gathers and displays key information about the system โ perfect for quick server audits.
#!/bin/bash
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
section() {
echo -e "\n${CYAN}โโโ $1 โโโ${NC}"
}
section "SYSTEM INFO"
echo "Hostname : $(hostname)"
echo "OS : $(uname -s) $(uname -r)"
echo "Uptime : $(uptime -p 2>/dev/null || uptime)"
echo "Date : $(date)"
section "CPU"
echo "Model : $(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)"
echo "Cores : $(nproc)"
echo "Load Avg : $(cat /proc/loadavg | awk '{print $1, $2, $3}')"
section "MEMORY"
free -h | awk '
/^Mem:/ {printf "Total: %s | Used: %s | Free: %s\n", $2, $3, $4}
/^Swap:/ {printf "Swap: %s | Used: %s | Free: %s\n", $2, $3, $4}
'
section "DISK"
df -h / | awk 'NR==2 {printf "Root: %s total, %s used (%s), %s free\n", $2, $3, $5, $4}'
section "NETWORK"
hostname -I 2>/dev/null | awk '{print "IP Address:", $1}' || echo "IP: N/A"
echo -e "\n${GREEN}โ System check complete${NC}"Project 2: Log Analyzer
Analyze web server access logs to find top visitors, most-requested pages, and error rates.
#!/bin/bash
set -euo pipefail
LOGFILE="${1:?Usage: $0 <access.log>}"
if [[ ! -f "$LOGFILE" ]]; then
echo "Error: File not found: $LOGFILE" >&2
exit 1
fi
total=$(wc -l < "$LOGFILE")
echo "=== Log Analysis: $LOGFILE ==="
echo "Total requests: $total"
echo ""
echo "--- Top 10 IP Addresses ---"
awk '{print $1}' "$LOGFILE" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- Top 10 Requested URLs ---"
awk '{print $7}' "$LOGFILE" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- HTTP Status Code Breakdown ---"
awk '{print $9}' "$LOGFILE" | sort | uniq -c | sort -rn
echo ""
echo "--- Error Rate (4xx + 5xx) ---"
errors=$(awk '$9 ~ /^[45]/ {count++} END {print count+0}' "$LOGFILE")
if (( total > 0 )); then
rate=$(echo "scale=2; $errors * 100 / $total" | bc)
echo "$errors errors out of $total requests ($rate%)"
else
echo "No requests to analyze."
fi
echo ""
echo "--- Requests Per Hour ---"
awk -F: '{print $2}' "$LOGFILE" | awk '{print $1}' | sort | uniq -c | sort -k2nProject 3: Automated Backup Script
A production-ready backup script with rotation, logging, and error handling.
#!/bin/bash
set -euo pipefail
# Configuration
SOURCE_DIR="${1:-$HOME/documents}"
BACKUP_DIR="${2:-$HOME/backups}"
MAX_BACKUPS=7
LOG_FILE="$BACKUP_DIR/backup.log"
# Setup
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_FILE="$BACKUP_DIR/backup_$TIMESTAMP.tar.gz"
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
err() {
log "ERROR: $1"
exit 1
}
# Cleanup on exit
trap '[[ $? -ne 0 ]] && log "Backup FAILED"' EXIT
# Validate
[[ -d "$SOURCE_DIR" ]] || err "Source directory not found: $SOURCE_DIR"
mkdir -p "$BACKUP_DIR"
log "Starting backup of $SOURCE_DIR"
# Create backup
tar -czf "$BACKUP_FILE" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")" \
2>> "$LOG_FILE"
# Verify
if [[ -f "$BACKUP_FILE" ]]; then
size=$(du -h "$BACKUP_FILE" | cut -f1)
log "Backup created: $BACKUP_FILE ($size)"
else
err "Backup file was not created"
fi
# Rotate โ keep only the last N backups
backup_count=$(ls -1 "$BACKUP_DIR"/backup_*.tar.gz 2>/dev/null | wc -l)
if (( backup_count > MAX_BACKUPS )); then
ls -1t "$BACKUP_DIR"/backup_*.tar.gz | tail -n +$((MAX_BACKUPS + 1)) | while read -r old; do
log "Removing old backup: $old"
rm -f "$old"
done
fi
log "Backup complete. Total backups: $(ls -1 "$BACKUP_DIR"/backup_*.tar.gz | wc -l)"0 2 * * * /home/user/backup.sh >> /var/log/backup.log 2>&1
Best Practices Recap
- Always use
set -euo pipefailโ catch errors early. - Quote your variables โ
"$var"prevents word splitting and glob expansion. - Use functions โ keep scripts modular and testable.
- Log to stderr โ keep stdout clean for data, stderr for messages.
- Use
trapfor cleanup โ always clean up temp files. - Add a usage/help function โ scripts should be self-documenting.
- Use
[[ ]]over[ ]โ fewer surprises with empty variables. - Use
shellcheckโ a linting tool that catches common bugs.
Security Considerations
- Never store passwords in scripts. Use environment variables or secret managers.
- Be cautious with
evalโ it executes arbitrary code and is a common injection vector. - Validate all user input before using it in commands.
- Use
mktempfor temporary files instead of hardcoded names. - Set restrictive permissions on scripts containing sensitive logic:
chmod 700 script.sh.
eval with untrusted input. A string like $(rm -rf /) inside an eval statement would be catastrophic.
Next Steps & Resources
- ShellCheck (shellcheck.net) โ Lint your scripts online or install locally.
- Advanced Bash-Scripting Guide โ The classic reference (tldp.org).
- Explainshell (explainshell.com) โ Paste a command and get a breakdown.
- Practice: Automate something you do manually. That's how real learning happens.
Try It Yourself: Build a Mini Script
Combine everything you've learned into a script that accepts a directory, counts files by extension, and reports the results.
Congratulations! ๐
You've completed the entire BashHero curriculum! You've gone from your first echo to building production-ready scripts with error handling, automation, and real-world projects. The command line is now your playground. Keep practicing, keep automating, and keep learning!