Control and functions

Enable safer failure behavior

Use unset-variable and pipeline checks while understanding that automatic exit is not a substitute for error handling.

10 minute lesson

~~~

By default, Bash keeps going. A typo expands to an empty string, a failing pipeline reports success, and a broken command in the middle of a script just scrolls past. Bash defaults can hide failures. Two settings tighten this up: set -u rejects unset variables, and set -o pipefail makes a pipeline reflect failing stages.

Start a script with explicit policy:

#!/usr/bin/env bash
set -u
set -o pipefail

What set -u catches

Create an unset variable in a disposable script and watch:

target=/var/www
rm -rf "$targt/cache"
# bash: targt: unbound variable

Without -u, the misspelled $targt expands to nothing and the command becomes rm -rf /cache — a different directory entirely. With -u, the script stops at the typo. This one behavior pays for itself for years.

Legitimately optional variables still work: write ${1:-} or ${DEBUG:-0} to supply an explicit default.

What pipefail catches

A pipeline normally reports only the status of its last command:

cat /nonexistent/data.txt | wc -l
# cat: /nonexistent/data.txt: No such file or directory
# 0
printf '%s\n' "$?"
# 0 without pipefail — wc succeeded, so cat's failure vanished
# 1 with pipefail — the failing stage comes through

With set -o pipefail, a failing pipeline returns the status of the last command that failed. Create a failing pipeline like this one in your disposable script and confirm the difference yourself.

What about set -e?

You will see the trio written as set -euo pipefail. The -e part exits the script when a command fails, and it sounds perfect. Treat set -e carefully, though: its exceptions depend on command context. A failure inside an if condition, on either side of && or ||, or in a negated command does not trigger an exit — and that surprises people constantly.

My advice: use set -u and set -o pipefail everywhere. Add -e as a safety net if you like it, but don’t call it error handling. Explicit if checks are clearer around recovery paths:

if ! cp nginx.conf /etc/nginx/nginx.conf; then
  printf '%s\n' 'config copy failed, keeping the old file' >&2
  exit 1
fi

Add deliberate checks around failures you expect. Automatic exit can’t decide what should happen next — you can.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →