DevOps Tutorial · Linux and the Shell · Lesson 8 of 25

Writing Robust Scripts

Make bash scripts fail fast and clean up after themselves with set -euo pipefail, traps, quoting and ShellCheck.

Create a free account to save this page, take notes and track your progress. Sign in

Fail fast

By default bash keeps going after a failed command, which is dangerous in automation: a failed cd followed by rm -rf * deletes the wrong directory. Start scripts with:

#!/usr/bin/env bash
set -euo pipefail
  • -e exit when a command fails.
  • -u treat unset variables as errors (catches typos like $DEPLOY_DIRR).
  • -o pipefail a pipeline fails if any command in it fails, not just the last.
What set -euo pipefail changes
cat > without.sh <<'SCRIPT'
false | true
echo "without pipefail: the failed pipeline was ignored"
SCRIPT
cat > with.sh <<'SCRIPT'
set -euo pipefail
false | true
echo "never printed"
SCRIPT
bash without.sh
bash with.sh || echo "with pipefail: script stopped (exit code $?)"
bash -c 'set -u; echo "$UNDEFINED_VAR"' 2>&1 | sed 's/^.*line [0-9]*: //' || true

Output

without pipefail: the failed pipeline was ignored
with pipefail: script stopped (exit code 1)
UNDEFINED_VAR: unbound variable

Run this on your computer — see “Get Started” for setup.

Clean up with trap

trap ... EXIT runs whatever happens, so temporary files and locks are always removed.

Cleanup that always runs
cat > job.sh <<'SCRIPT'
set -euo pipefail
workdir=$(mktemp -d)
trap 'rm -rf "$workdir"; echo "cleaned up temp dir"' EXIT
echo "working in a temp dir"
false        # something fails half-way
echo "never printed"
SCRIPT
bash job.sh || echo "job failed with exit code $?, but cleanup still ran"

Output

working in a temp dir
cleaned up temp dir
job failed with exit code 1, but cleanup still ran

Run this on your computer — see “Get Started” for setup.

More habits

  • Quote every variable: "$var", "${array[@]}".
  • Use mktemp for temporary files, never fixed names in /tmp.
  • Check scripts with ShellCheck (shellcheck deploy.sh) in CI; it catches most quoting and logic bugs.
  • When a script grows past a page or needs data structures, switch to Python.

Test yourself

In a script with set -o pipefail, what is the exit status of false | true?

  1. 0 (success)
  2. Non-zero (failure)
  3. It depends on the shell
  4. The pipeline never finishes
Show answer

Answer: Non-zero (failure)

Without pipefail only the last command counts; with it, any failing command fails the pipeline.

Was this page helpful?