There's a set of things every bash script should have at the top. Not "here are some nice-to-haves" — actual things that prevent real, bad, hard-to-debug failures:
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
set -e makes the script exit immediately if any command returns a non-zero status. Without it, your script keeps running after a failure and you get compounding errors that are confusing to trace.
set -u makes the script error if you reference a variable that hasn't been set. Without it, $MYVAR being undefined silently evaluates to an empty string. That empty string goes into a path. That path gets passed to rm -rf. You see where this goes.
set -o pipefail makes the exit status of a pipeline reflect the first failure in the chain, not just the last command. broken_command | grep something would exit 0 without this flag, because grep succeeded even though the input was empty.
IFS=$'\n\t' prevents word splitting on spaces in filenames. Without it, a filename like my file.txt gets treated as two arguments.
This is what I call the mandatory header. I've forgotten parts of it enough times that I stopped trying to remember it and started generating it.
The Boilerplate Generator
You configure:
Script identity — name, description, author. These go in the comment block at the top so someone reading the script 6 months from now (probably you) knows what it's supposed to do.
Safety flags — each one is a checkbox with an explanation of what it does. set -x is there too for debug mode — I often add it temporarily when something isn't working, then remove it before deploying.
Features:
- Timestamped
log()function — stops you from using rawechofor output, which doesn't timestamp. When you're looking at a log file and everything just says "backup complete" with no time attached, you'll wish you had this. -
CHECK/CROSSvariables — addsCHECK="✓"andCROSS="✗"at the top, which is the convention across all BashSnippets scripts. Consistent visual output in the terminal. - Argument parser with
--helpand--dry-run— getopts-based. Dry run mode is something I use constantly during development to test a script's logic without actually writing/deleting/moving anything. - Lock file — prevents duplicate runs. If you're scheduling something with cron and the job sometimes runs long, without a lock file you can end up with two instances trying to write to the same output file.
- Root check — exits cleanly with an error if the script isn't run as root, instead of failing halfway through with a permission error.
- Trap on EXIT — runs a
cleanup()function on exit or interruption, which you can populate with whatever teardown logic makes sense.
Top comments (0)