<aside> 🛡️

Module 14 — Error Handling and Defensive Scripting. set -euo pipefail, ShellCheck, mktemp, lock files — the difference between a script that works on your laptop and one you trust to run unattended at 3 a.m.

🧠 concept → 🧩 analogy → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (hidden)

</aside>

<aside> 📋

Before you start. You need Modules 1–13 — this module is the payoff of the track's longest-running promises: the carry-on-past-failure default (Modules 1, 3, 6), the silent empty expansion (Module 2), the lying pipeline (Module 4), ${var:?} guards (Module 9), EXIT traps (Module 13). The empty slot in Module 8's template ("strict-mode flags will land here") gets filled today. Tools: ShellCheck — install it now: sudo apt install shellcheck (Debian/Ubuntu) or see shellcheck.net for other systems.

</aside>

Part A — Strict mode: three switches that change the default

A1. set -e, set -u, set -o pipefail

<aside> 📖

Official docs: Bash manual — The Set Builtin

</aside>

Bash's factory defaults were tuned for a human at a prompt: keep going after failures, treat unset variables as empty, let pipelines report only their last stage. Twelve modules have shown you what those defaults cost in scripts. Three switches, canonically written as one line at the top of the script, flip them:

set -euo pipefail

-e (errexit): any command failing (non-zero, unguarded) ends the script immediately with that verdict — the ;-glue parade (Module 3 B1) stops at the first fire. -u (nounset): expanding an unset variable becomes a fatal error instead of silent emptiness — Module 2's oldest hazard, finally loud. -o pipefail: a pipeline's verdict is its first failure, not its last stage's — Module 4's lying pipeline, cured. Together they invert the philosophy: instead of asking "did I remember to check this?", failures interrupt you unless you explicitly said they're fine.

<aside> 🧩

Real-world analogy — the factory dead-man switches. An old workshop keeps running when a machine jams — the line piles product onto the floor until a human notices. Strict mode installs dead-man switches: a jam (-e), a missing part in a feed (-u), a fault anywhere along a conveyor (pipefail) — each stops the line at the fault, with the fault indicated. Stopping is not the goal; stopping at the fault, before compounding damage, is.

Where the analogy stops working. Dead-man switches are dumb and total. set -e is neither — it deliberately ignores failures in guard positions (an if's condition, a &&/|| chain) because those failures are questions being answered, not accidents. Part B maps those exceptions precisely, because half of all strict-mode confusion is expecting a total switch and getting a discerning one.

</aside>

🧪 Exercise 14.1 — the three switches, thrown one at a time

All three scripts fail on purpose — that is now the point.

cd ~/bash-course
printf '#!/bin/bash\nset -e\necho "step 1"\nfalse\necho "step 2 (never)"\n' > se.sh
bash se.sh ; echo "verdict: $?"
bash -c 'set -u; echo "before"; echo "$TYPO_VAR"; echo "after"' ; echo "verdict: $?"
bash -c 'set -o pipefail; false | true; echo "pipefail verdict: $?"'

Interview questions — Part A

Part B — The sharp edges of -e (know them or be surprised)

B1. Where errexit deliberately looks away

<aside> 📖

Official docs: Bash manual — The Set Builtin (the -e paragraph lists every exception)

</aside>