LESSON · 1 OF 6

set -euo pipefail

set -euo pipefail

Three shell options that make scripts fail early and loudly instead of continuing with broken state. Together, they turn bash from "silently-ignore-errors" into "loudly-exit-on-error".

set -e: exit on error

bash
#!/bin/bash
set -e
mkdir -p /var/backups
cp source.db /var/backups/       # if this fails, script exits
process /var/backups/source.db

With set -e, any command returning nonzero (that isn't part of an if, &&, or ||) exits the whole script. Without it, the next command runs anyway, using state that might be broken.

set -u: undefined variables are errors

bash
set -u
echo "$name"    # if $name is unset, script exits with error

Catches typos in variable names. $namae (typo) triggers an error instead of expanding to empty and continuing silently.

Use ${var:-} when you actually want an empty default:

bash
optional=${OPTIONAL:-}

set -o pipefail: catch failures inside pipelines

bash
set -o pipefail
grep nonexistent /etc/hosts | sort | uniq

Without pipefail, only the LAST command's exit code counts. If grep fails but uniq succeeds, the pipeline is "successful" from bash's point of view. pipefail says: if ANY command in the pipeline fails, the pipeline as a whole fails.

The classic combo

bash
#!/bin/bash
set -euo pipefail

That single line at the top of every serious script:

  • -e exit on error
  • -u treat unset variables as errors
  • -o pipefail fail if any part of a pipeline fails

If you learn one line from this whole topic, that is the one.

When set -e bites

set -e has quirks. Commands in an if or after &&/|| don't trigger the exit, which is intentional. But some real gotchas exist:

bash
count=$(grep -c ERROR log.txt)    # if no matches, grep exits 1
                                   # set -e kills the script here

Workaround:

bash
count=$(grep -c ERROR log.txt || true)

|| true swallows the failure. Ugly but effective.

Spin up a fresh environment and practice live.
linux-devops-basic · fresh machine · ready in under a minute