Step 1 of 5 · Reading · ~4 min
Learn
Arrays and Scripts
Script Hygiene
By the end of this lesson you can turn on bash's strict mode, explain what each of its three switches protects you from, and write the few idioms that keep a strict script from dying on failures you expected. Every lesson so far showed how scripts work. This one is about how they fail silently, and the short preamble professionals paste at the top of every script to make failures loud, early and safe.
The unofficial strict mode
#!/usr/bin/env bash
set -euo pipefail
Three switches, each closing a real hole:
set -e: exit as soon as a command fails. By default bash keeps going after errors, socd /nope; rm -rf *fails thecdand then runs thermin the wrong directory. With-ethe script stops at thecd.set -u: using an unset variable is an error. By default a typo expands to an empty string, sorm -rf "$BUILD_DRI/"quietly becomesrm -rf "/". With-ubash stops withBUILD_DRI: unbound variable.set -o pipefail: a pipeline fails if any stage fails. By default only the last stage counts, sogrep x missing-file | sortreports success becausesortworked.
set -e has exemptions: a command used as the condition of an if, while, && or || may fail without stopping the script. The pragmatic consensus is to use strict mode and learn the exemptions as you meet them. A script that dies too eagerly produces a bug report; a script that plows on produces an incident.
Strict mode makes normal failures fatal
Some tools use a non-zero status for an ordinary answer, and under -e that answer ends your script:
set -euo pipefail
hits=$(grep -c "timeout" server.log) # no matches: grep prints 0 but EXITS 1, script dies here
echo "unreached"
When a failure is acceptable, say so explicitly. The idiom is || true, which runs true (always successful) only if the left side fails:
hits=$(grep -c "timeout" server.log || true) # hits is 0, script continues
Do not write || echo 0 here. grep -c has already printed its own 0, so hits would hold the two lines 0 and 0.
A related trap: ((n++)) as a command has the status 1 when n was 0, because its value is 0 and arithmetic treats a zero result as "false". Under -e that kills the script on the first increment. Write n=$((n + 1)) instead.
For unset variables, -u is paired with expansions that supply a value:
name=${1:-guest} # $1 if set and non-empty, otherwise guest
dir=${BACKUP_DIR:?set BACKUP_DIR first} # fail with this message if unset or empty
Quote everything
You have heard it every lesson; here it is as policy: "$var", "${arr[@]}", "$@", always. Unquoted expansion with word-splitting and globbing is bash's largest bug class, and quoting is its complete cure. Related armour: mkdir -p (no error if the directory exists), rm -f (no error if the file is absent), and -- to end option parsing, so rm -- "$file" survives a file called -rf.
shellcheck: the reviewer that never sleeps
$ shellcheck deploy.sh
ShellCheck is a static analyser that knows the traps in this course and hundreds more, each with a page that explains why. It runs in editors, in CI and at shellcheck.net. Clean shellcheck output is the nearest bash gets to a compiler.
Fail with useful messages
die() {
echo "ERROR: $*" >&2 # errors go to stderr, never into the results
exit 1
}
[[ -f "$config" ]] || die "config not found: $config"
The message goes to stderr and the non-zero exit lets callers, and set -e, notice. $* joins all the arguments into one string.
Common mistakes
- Strict mode without
|| true. The firstgrepthat finds nothing ends the script. || echo 0aftergrep -c. It produces0twice.((n++))from zero. It fails once, under-e, at the worst moment.- Testing an unset variable directly. Under
-u,[[ -z $x ]]aborts whenxis unset; use[[ -z ${x:-} ]]. - Errors on stdout. A message in the middle of the results corrupts whatever consumes them.
Your exercise
Summarise a log under strict mode. The two questions you ask the log both fail with status 1 when the answer is "nothing found", which is a normal outcome here and must not stop the script. Decide for each command what failure means, and make the empty cases print a proper answer.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…