Every script in this final topic follows the same shape. Learn it once, apply it forever.
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
readonly SCRIPT_NAME="${0##*/}"
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SCRIPT_NAME and SCRIPT_DIR so the script knows where it lives.die() {
echo "$SCRIPT_NAME: $*" >&2
exit 1
}
log() {
echo "[$(date +%H:%M:%S)] $*"
}
Two utility functions almost every script wants. die for
fatal errors, log for timestamped normal output.
Even for scripts with one argument, validate:
[[ $# -eq 1 ]] || die "usage: $SCRIPT_NAME <target>"
target=$1
For flags, use getopts from topic 4.
If the script writes to /tmp or holds locks:
tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' EXIT
main() {
parse_args "$@"
do_the_thing
finish_up
}
main "$@"
Wrapping the top-level logic in main (and calling it at the
end) makes the script easier to read and easier to test.
Functions defined above main are visible when main runs.
The next three nodes are complete production-quality scripts: a backup script, a health check, and a deploy script. Each one follows this shape and exercises multiple topics you've learned. Read the code, run it, and see how the pieces fit together.