Keeping shell scripts honest
The first line of every script I write is set -euo pipefail. Exit on
the first failing command, treat undefined variables as errors, and do not let a
failing command in the middle of a pipeline disappear silently. Without these three
flags a script does not fail — it continues into a state you did not plan for.
The first bug I keep making anyway: unquoted variables. rm -rf $DIR
works until DIR contains a space, and then it works in ways you will remember for a
long time. Quote everything: rm -rf "$DIR". There is no performance
penalty for being correct.
The second: temp directories without cleanup. mktemp -d plus a
trap 'rm -rf "$TMP"' EXIT at the top of the script, always. Without the
trap, every aborted run leaves garbage behind, and one day the garbage is half a
disk.
The third: assuming a binary exists. A script that works on the laptop fails on
the server because jq is not installed there. A one-line
command -v jq >/dev/null || { echo "jq required"; exit 1; } turns a
mysterious mid-script failure into a clear message.
Traps deserve more credit in general: cleanup on EXIT, a debug mode via
set -x behind an environment variable, locking with
flock so two cron runs do not collide. None of it is exotic; all of it
is one line each.
And run shellcheck before every commit. It finds the first two bugs every single time, which suggests I will keep making them, which suggests shellcheck stays.