Skip to content
Case Files
PUBLIC2026.07.04·2 MIN READ

The Four Lines at the Top of Every Bash Script I Write

Bash defaults let a script fail silently and keep going. These four lines make it stop the moment something's wrong.

  • #bash
  • #shell
  • #reliability

Bash will let you do almost anything, including a lot of damage, and it won’t warn you first. By default a script keeps running after a command fails, treats a misspelled variable as an empty string, and calls a pipeline successful as long as the last command in it worked, even if everything before it blew up.

I learned this the usual way. A backup script tried to cd into a directory that wasn’t there. The cd failed, the script kept going, and the next line ran rm -rf ./* from the wrong place. Nothing errored. Exit code zero. That’s when I started putting the same header on every script.

#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'

Four small choices, each one closing off a specific way to hurt yourself.

set -e, stop on the first failure

Without it, a failed command is just a suggestion. The script keeps going and runs later steps against a state that isn’t what they expect. set -e makes a non-zero exit code do the obvious thing and stop, so you fail where the problem is instead of three steps later where you can’t recognize it anymore.

set -u, a typo’d variable is an error

By default, writing $DEPLOY_DIR when you meant $DEPLOY_PATH expands to nothing. So rm -rf "$DEPLOY_DIR/cache" quietly becomes rm -rf /cache. set -u turns any reference to an unset variable into an error right away instead of a silent empty string. This one has saved me more than the other three combined.

set -o pipefail, the whole pipeline has to pass

This is the sneaky one. Take this:

curl -s https://example.com/data | grep "prod" | sort

If curl fails outright, grep and sort still run happily on nothing, and the pipeline reports success. pipefail makes it fail if any stage fails, not just the last one. Without it, “the download came back empty and we processed zero records like that was normal” is a real thing that can happen to you.

IFS=$'\n\t', stop splitting on spaces

The default IFS splits on spaces, so a filename like quarterly report.pdf becomes two arguments the moment it isn’t quoted. Setting IFS to newline and tab makes loops over lists behave the way you’d expect. You should still quote your variables, but this is a good backstop.

One more line for anything that matters

Once the script can fail safely, give it a way to clean up after itself. trap runs a function when the script exits, whether it finished, errored, or got killed:

cleanup() {
  rm -f "$TMPFILE"
}
trap cleanup EXIT

Now temp files and lock files get cleaned up even when the script dies partway through. Which, thanks to the four lines above, it now does loudly and on purpose instead of quietly.


Back to Case Files