Bash set Command: set -e, set -x, and set -u Explained

By default, a bash script keeps running even after a command fails. A typo in a variable name expands to an empty string, a failed cd is ignored, and the script marches on as if nothing happened, often doing real damage by the time it stops. The set builtin changes that. With a few flags, you can make bash stop on the first error, treat unset variables as mistakes, and print each command as it runs.
This guide explains the set command and the three options you will use most: set -e, set -u, and set -x, plus pipefail and how to combine them into a strict mode.
What the set Command Does
set is a shell builtin that turns shell options on or off and sets the positional parameters. For options, it takes this form:
set [options]Each option has a short form and a long form. A leading dash turns an option on, and a leading plus turns it off, which is the reverse of what most people expect:
set -eenables an option (here, exit on error).set +edisables it.set -o errexitis the long form ofset -e.set +o errexitis the long form ofset +e.
You place these near the top of a script so they apply to everything that follows, or around a specific block when you only want them in part of the script.
set -e: Exit on Error
set -e (also written set -o errexit) tells bash to exit immediately if any command returns a non-zero status. It stops a script from continuing past a failure.
Consider a script that changes into a directory and then writes a log message:
#!/bin/bash
set -e
cd /var/cache/myapp
printf 'Cleaning application cache\n'Without set -e, if the cd fails because the directory does not exist, the script would continue and run the next command in whatever directory it started in. With set -e, the failed cd stops the script before the log message or any later cache command runs.
There are important exceptions where set -e does not trigger an exit, and they trip people up. A command does not cause an exit when it is:
- part of an
if,while, oruntiltest, - joined with
&&or||, - preceded by
!.
This is by design, so that you can test a command’s exit status. If you need a specific command’s failure to be tolerated, append || true:
grep "pattern" file.txt || trueset -u: Treat Unset Variables as Errors
set -u (or set -o nounset) makes bash exit when you reference a variable that has not been set. This catches one of the most common scripting bugs: a typo in a variable name that silently expands to nothing.
#!/bin/bash
set -u
target_dir="/srv/www"
printf 'Target cache directory: %s\n' "${tagret_dir}/cache"The variable name is misspelled as tagret_dir. Without set -u, ${tagret_dir} expands to an empty string, and the command prints /cache instead of /srv/www/cache. With set -u, bash stops with an error instead:
deploy.sh: line 5: tagret_dir: unbound variableWhen a variable is legitimately optional, provide a default with ${VAR:-default} so the reference is always defined:
echo "Deploying to ${ENVIRONMENT:-staging}"set -x: Trace Each Command
set -x (or set -o xtrace) prints every command to standard error before it runs, with the expanded values of any variables. It is the fastest way to see exactly what a script is doing without adding echo statements everywhere.
#!/bin/bash
set -x
name="Linuxize"
echo "Hello, $name"Running the script shows each command, prefixed with a +, alongside its normal output:
+ name=Linuxize
+ echo 'Hello, Linuxize'
Hello, LinuxizeThe trace shows the variable already expanded, so you see the real value bash used. The prefix comes from the PS4 variable. Set it to include the script name and line number, which is invaluable in longer scripts:
PS4='+ ${BASH_SOURCE}:${LINENO}: 'Because tracing is noisy, it is common to enable it only around the section you are debugging with set -x and then turn it off again with set +x.
set -o pipefail: Catch Failures in a Pipeline
By default, a pipeline returns the exit status of its last command, so a failure earlier in the pipe is hidden. In the pipeline below, wc succeeds even though grep failed because the file does not exist:
grep "ERROR" missing.log | wc -lWithout pipefail, the pipeline exits with the status from wc, which is 0. set -o pipefail changes this so the pipeline returns the status of the rightmost command that failed, or 0 if every command succeeded. Combined with set -e, it makes a failed first stage actually stop the script.
Combine Them into a Strict Mode
These options are most useful together. The common combination at the top of a careful script is:
#!/bin/bash
set -euo pipefailThis enables exit-on-error, unset-variable checking, and pipeline failure detection in one line. Add x as set -euxo pipefail while debugging, then remove it. This pattern is the foundation of what is often called “bash strict mode”, which our bash strict mode
guide covers in more depth, including the trade-offs of set -e.
Enable and Disable Options for a Block
You do not have to apply an option to the whole script. Turn one on for a block and off afterward. This is common with tracing:
set -x
deploy_application
sync_assets
set +xOnly the two commands between set -x and set +x are traced. The same pattern works with set -e when you have a section where you want to handle errors manually.
View the Current Options
Running set -o with no flag prints every option and whether it is on or off. The full list has more than two dozen entries; the four options discussed here are:
set -oerrexit off
nounset off
pipefail off
xtrace offThis is a quick way to confirm which options an interactive shell or a sourced script has enabled.
Set Positional Parameters
The set command has a second job unrelated to options: when given arguments after --, it replaces the positional parameters $1, $2, and so on. This is useful when you want to reset the arguments a script or function reads:
set -- one two three
echo "$2"twoThe -- marks the end of options so that arguments starting with a dash are not mistaken for flags.
Quick Reference
For a printable quick reference, see the Bash cheatsheet .
| Option | Long form | Effect |
|---|---|---|
set -e | set -o errexit | Exit immediately on a command failure |
set -u | set -o nounset | Error when an unset variable is used |
set -x | set -o xtrace | Print each command before running it |
set -o pipefail | set -o pipefail | Fail a pipeline if any command in it fails |
set +e | set +o errexit | Disable an option (plus instead of dash) |
set -o | List all options and their state | |
set -- a b c | Set the positional parameters |
FAQ
What is the difference between set -e and set -o errexit?
They are the same thing. set -e is the short form and set -o errexit is the long, more readable form. Use whichever you prefer; long forms are common in shared scripts because they are self-documenting.
Why does my script still continue after a command fails with set -e?
The failed command is probably part of an if test, joined with && or ||, or preceded by !. In those positions bash deliberately ignores the failure so you can test exit status. A command substitution or a function call can also mask the failure.
What does the plus sign do, as in set +x?
A plus disables an option that a dash would enable. set -x turns tracing on and set +x turns it off, so you can scope an option to part of a script.
Should I always use set -euo pipefail?
It is a good default for new scripts and catches many bugs early, but set -e in particular has surprising edge cases. For complex scripts, understand how each option behaves and handle the exceptions explicitly rather than assuming the script is fully protected.
Conclusion
The set builtin turns bash from a forgiving shell into a strict one: set -e stops on errors, set -u catches typos in variable names, and set -x shows you exactly what ran. Combine them as set -euo pipefail at the top of a script, scope tracing to the block you are debugging, and your scripts will fail loudly and early instead of quietly doing the wrong thing. For more on writing reliable scripts, see our guides on bash best practices
and bash functions
.
Tags
Linuxize Weekly Newsletter
A quick weekly roundup of new tutorials, news, and tips.
About the authors

Dejan Panovski
Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.
View author page