Bulletproof Bash: set -euo pipefail & traps
Turn a fragile script into a robust one with strict mode, traps, arrays and proper quoting.
Most bash scripts fail silently: a command errors, the script keeps marching on, and you find out hours later when the data is already wrong. A few lines at the top change everything.
Strict mode
Start every serious script with what people call strict mode:
set -e— exit the moment any command fails.set -u— treat an unset variable as an error instead of an empty string (catches typos like$fille).set -o pipefail— a pipeline fails if any stage fails, not just the last one. Without this,false | truereports success.
Combined as set -euo pipefail. Add IFS=$'\n\t' to stop word-splitting on spaces.
Clean up with traps
A trap runs a function when the script exits or is interrupted — perfect for removing temp files or releasing a lock no matter how the script ends.
Arrays beat space-separated strings
Store lists in a real array and always expand it as "${arr[@]}". This keeps filenames with spaces intact — the single most common source of "it worked on my machine" bugs.
Rule of thumb: if a variable might ever hold a path, quote it. Every time."$var"is almost never wrong; a bare$varvery often is.
Example
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
# Create a temp workspace and guarantee it is cleaned up
workdir="$(mktemp -d)"
cleanup() {
local code=$?
rm -rf "$workdir"
echo "cleaned up, exit code $code"
}
trap cleanup EXIT
# An array survives filenames with spaces
files=("annual report.pdf" "notes.txt" "data set.csv")
for f in "${files[@]}"; do
echo "processing: $f"
cp "$f" "$workdir/" # quotes keep each name whole
done
# A command allowed to fail must say so out loud
grep -q pattern config.ini || echo "pattern not found (that is fine)"When to use it
- A DevOps engineer adds 'set -euo pipefail' to a database migration script so it stops immediately if any SQL command fails instead of silently continuing.
- A sysadmin uses a trap to remove a lock file when a backup script exits, ensuring the lock is always cleaned up even on errors.
- A CI script uses arrays to collect failed service names across a loop and reports them all at the end, avoiding string concatenation bugs.
More examples
Strict mode with error trap
Combines set -euo pipefail with an ERR trap to stop execution and report the exact line where a command failed.
#!/bin/bash
set -euo pipefail
# Trap any error and print line number
trap 'echo "ERROR on line $LINENO" >&2; exit 1' ERR
cp config.yaml /etc/myapp/
systemctl restart myapp
echo "Deploy complete"Cleanup trap on EXIT
Registers an EXIT trap to delete a lock file, guaranteeing cleanup whether the script succeeds or fails.
#!/bin/bash
set -euo pipefail
LOCKFILE=/var/run/myjob.lock
# Always remove lock file when script exits
trap 'rm -f "$LOCKFILE"' EXIT
touch "$LOCKFILE"
echo "Running exclusive job..."
sleep 10
echo "Done"Use arrays to avoid word splitting
Collects find output safely into a Bash array using null-delimited records to handle filenames with spaces.
#!/bin/bash
set -euo pipefail
# Store files with spaces safely in an array
FILES=()
while IFS= read -r -d $'\0' f; do
FILES+=("$f")
done < <(find /data -name '*.csv' -print0)
echo "Found ${#FILES[@]} CSV files"
for f in "${FILES[@]}"; do
echo "Processing: $f"
done
Discussion