Every deployment pipeline, every CI job, every server bootstrap starts with a shell script. And most of them are brittle, unreadable disasters that fail silently at 3 AM. Shell scripting is not a dying art. It is the glue that holds infrastructure together. Writing it well is a superpower.
A shell script that fails silently is worse than no script at all.
The first line of every script should set strict mode. set -euo pipefail. This single line transforms bash from a language that ignores errors into one that fails fast. -e exits on error. -u treats unset variables as errors. -o pipefail catches failures in piped commands. If you write bash without these flags, you are writing bugs.
#!/usr/bin/env bash
set -euo pipefail
# -e: exit immediately on non-zero status
# -u: treat unset variables as errors
# -o pipefail: catch errors in pipelines
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly LOG_FILE="/var/log/deploy-$(date +%Y%m%d-%H%M%S).log"
# Redirect all output to log + stdout
exec > >(tee -a "$LOG_FILE") 2>&1
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting deployment..."
# Validate prerequisites before doing anything
command -v docker >/dev/null 2>&1 || { echo "Docker required"; exit 1; }
command -v git >/dev/null 2>&1 || { echo "Git required"; exit 1; }
# Use mktemp for safe temporary files
TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT
git clone --depth 1 "$REPO_URL" "$TMP_DIR/repo"
cd "$TMP_DIR/repo"
docker build -t "$IMAGE_TAG" .
docker push "$IMAGE_TAG"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Deployment complete."The trap command is criminally underused. It ensures cleanup runs regardless of how the script exits — success, failure, or interrupt. Without trap, temporary files accumulate until the disk fills up. With it, your server stays clean even when things go wrong.
Quoting variables is non-negotiable. An unquoted variable with spaces splits into multiple arguments. A filename with spaces becomes two filenames. Always quote: "$variable", never $variable. This is the most common source of shell script bugs, and it is entirely preventable.
# WRONG: filename with spaces breaks everything
rm $filename
# CORRECT: treats the value as a single argument
rm "$filename"
# Arrays for multiple values
files=("file one.txt" "file two.txt" "file three.txt")
for f in "${files[@]}"; do
echo "Processing: $f"
doneShell scripts are not going away. Kubernetes, Docker, CI pipelines, server provisioning — they all depend on bash. The difference between a script that works and a script that works reliably is discipline. Fail fast, clean up after yourself, quote everything, and log obsessively. These are not suggestions. They are requirements.
Comments