Advanced Bash Scripting
Advanced bash scripting builds on fundamentals to create production-quality scripts that handle errors gracefully, clean up after themselves, process large files efficiently, and are safe to run in automated environments. The difference between a beginner script and a production script is mostly in error handling: what happens when a command fails, what state is left behind, and how much information is logged for debugging.
Error handling and traps
nano /usr/local/bin/safe-deploy.sh
safe-deploy.sh — trap-based cleanup
#!/bin/bash
set -euo pipefail
TEMP_DIR=$(mktemp -d) # Create unique temp directory
LOCK_FILE="/var/run/deploy.lock"
# Cleanup function — runs on ANY exit (success or failure):
cleanup() {
local exit_code=$?
rm -rf "${TEMP_DIR}"
rm -f "${LOCK_FILE}"
if [[ $exit_code -ne 0 ]]; then
echo "FAILED: deploy script exited with code ${exit_code}" >&2
fi
}
# Register cleanup to run on EXIT, INT (Ctrl+C), TERM:
trap cleanup EXIT INT TERM
# Prevent concurrent runs:
if [[ -f "${LOCK_FILE}" ]]; then
echo "Another deploy is running (lock: ${LOCK_FILE})" >&2
exit 1
fi
touch "${LOCK_FILE}"
echo "Deploying to ${TEMP_DIR}..."
# ... deployment commands here ...
echo "Deploy complete"
Arrays and string manipulation
# Bash arrays:
SERVERS=("web-01" "web-02" "db-01")
# Loop over array:
for server in "${SERVERS[@]}"; do
echo "Checking ${server}..."
ssh "${server}" uptime
done
# Array length:
echo "Server count: ${#SERVERS[@]}"
# String manipulation:
FILENAME="backup-2025-06-09.tar.gz"
echo "${FILENAME%%.*}" # backup-2025-06-09 (remove longest suffix match .*)
echo "${FILENAME##*.}" # gz (remove longest prefix match *.)
echo "${FILENAME/backup/archive}" # archive-2025-06-09.tar.gz (replace)
DATE_STR="2025-06-09"
YEAR="${DATE_STR:0:4}" # 2025 (substring: start, length)
MONTH="${DATE_STR:5:2}" # 06
# Parse CSV output:
while IFS=',' read -r name size status; do
echo "Name: ${name}, Size: ${size}, Status: ${status}"
done < servers.csv
File and log processing
nano /usr/local/bin/log-summary.sh
log-summary.sh — processing logs efficiently
#!/bin/bash
set -euo pipefail
LOG_FILE="${1:-/var/log/nginx/access.log}"
OUTPUT_FILE="/tmp/log-summary-$(date +%Y%m%d).txt"
# Check file exists and is readable:
if [[ ! -r "${LOG_FILE}" ]]; then
echo "Cannot read log file: ${LOG_FILE}" >&2
exit 1
fi
echo "Analyzing: ${LOG_FILE}" > "${OUTPUT_FILE}"
echo "Generated: $(date)" >> "${OUTPUT_FILE}"
echo "" >> "${OUTPUT_FILE}"
# Top 10 IP addresses:
echo "=== Top 10 Client IPs ===" >> "${OUTPUT_FILE}"
awk '{print $1}' "${LOG_FILE}" | sort | uniq -c | sort -rn | head -10 >> "${OUTPUT_FILE}"
# HTTP status code summary:
echo "=== Status Codes ===" >> "${OUTPUT_FILE}"
awk '{print $9}' "${LOG_FILE}" | sort | uniq -c | sort -rn >> "${OUTPUT_FILE}"
# 404 errors:
echo "=== 404 Not Found URLs ===" >> "${OUTPUT_FILE}"
awk '$9 == 404 {print $7}' "${LOG_FILE}" | sort | uniq -c | sort -rn | head -20 >> "${OUTPUT_FILE}"
cat "${OUTPUT_FILE}"
Script best practices
Production script checklist:
set -euo pipefail → fail fast on errors
trap cleanup EXIT → always clean up temp files and locks
[[ $# -ne N ]] → validate argument count
[[ -f "${file}" ]] → validate inputs before using them
local variables → scope variables inside functions
Log with timestamps → echo "[$(date)] ..." >> "${LOG_FILE}"
Redirect errors → echo "Error" >&2 (not stdout)
Lock files → prevent concurrent execution
Dry-run mode → add --dry-run flag that echoes commands instead of running
# Dry-run pattern (safe to test):
DRY_RUN=false
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
run_cmd() {
if $DRY_RUN; then
echo "[DRY-RUN] $*"
else
"$@"
fi
}
# Usage:
run_cmd rm -rf /old/directory
run_cmd systemctl restart nginx
Conclusion
The trap cleanup EXIT pattern is the single most important advanced technique for production scripts: it ensures temp files and lock files are always removed regardless of how the script exits — success, error, or Ctrl+C. Without it, failed scripts leave lock files that block future runs and temp files that fill disk. Combine this with lock files (/var/run/scriptname.lock) to prevent cron jobs from running concurrently when a previous instance is still running.
FAQ
Is Advanced Bash Scripting important for Ubuntu administrators?+
Yes. It supports practical Ubuntu administration because it connects directly to server reliability, security, troubleshooting, or daily operations.
Should I practice this on a live server?+
Use a lab VM first. After you understand the command output and rollback path, apply the workflow carefully on real systems.
What should I do after reading this article?+
Run the practice commands, write down what each one shows, and continue to the next article in the Ubuntu roadmap.
Need help with Ubuntu administration?
Work directly with Muhammad Irfan Aslam for Ubuntu Server, Linux, cloud, Docker, DevOps, CI/CD, or infrastructure troubleshooting support.
Hire Me for Support