| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | bash 5.2.26-6.el10 |
| Applies to | Linux distributions with Bash installed |
| Privilege | Normal user; no root required |
| Scope | Bash set and shopt options: inspection, enable/disable syntax, globbing behavior, lastpipe, inherit_errexit, and temporary option changes. Does not cover full strict-mode rules, pathname expansion patterns, or interactive tracing with bash -x. |
| Related guides | Bash shell script tutorial Shell scripting course hub |
In this article we will sort out why Bash has two builtins for changing shell behavior: set and shopt.
You will see set -o pipefail in one script and shopt -s nullglob in another. They look similar, but they control different option families. Use set for traditional shell attributes such as errexit, nounset, pipefail, and xtrace. Use shopt for additional optional Bash behavior such as nullglob, failglob, globstar, and lastpipe. The sections below show how to tell which command an option belongs to and how to enable, check, disable, and temporarily change it.
Bash set vs shopt — Quick Difference
| Tool | Typical options | What it controls |
|---|---|---|
set |
errexit, nounset, pipefail, xtrace, noclobber, noglob (-f) |
Bash's traditional shell attributes and single-letter flags |
shopt |
nullglob, failglob, globstar, extglob, dotglob, lastpipe, inherit_errexit |
Additional optional Bash behavior beyond the set -o set |
GNU Bash describes shopt as controlling optional shell behavior on top of the options you manage with set. set -o is not limited to POSIX-only flags; Bash exposes its own options such as pipefail through set as well.
shopt -o is the bridge between the two: it lists or changes options that set -o also recognizes. For example, shopt -o pipefail reports the same setting as set -o pipefail.
How do I know whether an option uses set or shopt?
When you are unsure which command to reach for, query both families:
set -o | grep pipefailpipefail offThe same name is visible through the shopt bridge:
shopt -o | grep pipefailpipefail offpipefail appears under set -o, so you enable it with set -o pipefail. Options that exist only under shopt—such as nullglob—never show up in set -o output:
shopt | grep nullglobnullglob offConfirm it does not appear under set -o:
set -o | grep nullglobThe grep finds nothing because nullglob is a shopt-only option. Use shopt -q nullglob to test a single shopt option quietly in a script.
set is not exclusively an option manager. It can also display shell variables and functions, and it can replace positional parameters. shopt, by contrast, exists only for optional Bash behavior.
Set three positional parameters and print the second one:
set -- one two threeset -- clears the old positional list and assigns the new words. Read the middle argument:
printf '%s\n' "$2"twoThat form of set has nothing to do with set -o. Option changes always use set -o name, set +o name, or the short flags such as set -e.
How to Check Bash Shell Options
List every set -o option and whether it is on or off:
set -o | head -5allexport off
braceexpand on
emacs on
errexit off
errtrace offThe same state appears as replayable commands from set +o:
set +o | head -5set +o allexport
set -o braceexpand
set -o emacs
set +o errexit
set +o errtraceList shopt options the same way:
shopt | grep -E 'nullglob|failglob|lastpipe|inherit_errexit'failglob off
inherit_errexit off
lastpipe off
nullglob offBash also keeps the enabled option sets in shell variables:
| Variable | Contains |
|---|---|
$SHELLOPTS |
Enabled set -o shell options |
$BASHOPTS |
Enabled shopt options |
$- |
Active single-character shell flags |
Print the current values:
echo "SHELLOPTS=$SHELLOPTS"SHELLOPTS=braceexpand:hashall:interactive-commentsEnabled shopt options appear in $BASHOPTS:
echo "BASHOPTS=$BASHOPTS"BASHOPTS=checkwinsize:cmdhist:complete_fullquote:extquote:force_fignore:progcomp:sourcepathActive single-character flags also appear in $-:
echo "$-"The exact letters depend on how Bash was invoked and whether the shell is interactive. For example, e appears when errexit is enabled, u for nounset, and x for xtrace.
$SHELLOPTS and $BASHOPTS are colon-separated lists of enabled option names.
Enable and Disable Options with set
With set -o, a leading - turns an option on and + turns it off. Short single-letter flags follow the same rule: set -e enables errexit; set +e disables it.
Watch errexit stop a short script on the first failing command:
bash -c 'set -e; false; echo survived'The command exits with status 1 and never prints survived.
Disable errexit and the same failure is ignored:
bash -c 'set +e; false; echo survived'survivedset +e is the explicit off switch. Errexit has important exceptions, which are covered separately in the Bash strict mode guide.
Enable and Disable Options with shopt
s sets a shopt option on, u sets it off, p prints the command that would restore the current value, and q tests quietly.
Print restore commands for a few script-related options:
shopt -p nullglob failglob lastpipe inherit_errexitshopt -u nullglob
shopt -u failglob
shopt -u lastpipe
shopt -u inherit_errexitUse shopt -q in conditionals instead of parsing shopt display text:
shopt -q nullglob; echo "nullglob off: exit $?"nullglob off: exit 1When the option is off, shopt -q returns exit status 1. Turn it on and the query succeeds:
shopt -s nullglob; shopt -q nullglob; echo "nullglob on: exit $?"nullglob on: exit 0Reset nullglob before continuing:
shopt -u nullglobNote that shopt -p option also returns a non-zero status when the option is disabled. In a set -e script, capture its output carefully or test with shopt -q first.
Useful Globbing Options
Pathname expansion details live in the Bash globbing guide. Here we focus on what shopt changes when a pattern has no match, and how that differs from set -f.
| Option | Effect |
|---|---|
nullglob |
Remove unmatched patterns (zero words) |
failglob |
Treat unmatched patterns as an error |
globstar |
Let ** recursively match directories |
extglob |
Enable extended patterns such as @(a|b) |
dotglob |
Include hidden files (names starting with .) in glob matches |
See the globbing guide for globstar, extglob, dotglob, and safe loops over matched files.
nullglob vs failglob
With default settings, an unmatched pattern stays literal:
printf '%q\n' *.xyz\*.xyzBash passes the literal pattern *.xyz to printf because no file matched.
With nullglob, an unmatched pattern disappears from the argument list:
shopt -s nullglob; printf '%q\n' *.xyz; shopt -u nullglob''printf received no arguments, so it printed a blank line. Loops like for f in *.tmp stop iterating when the directory has no .tmp files instead of running once with a literal *.tmp.
failglob takes the stricter approach and treats an unmatched pattern as an expansion error. Run the demo in a child shell so failglob does not stick in your current session:
bash -c 'shopt -s failglob; printf "%q\n" *.xyz'bash: line 1: no match: *.xyzThe child shell exits with a non-zero status, while your current shell's failglob setting stays unchanged. failglob is useful when zero matches means something went wrong, but do not enable it globally in scripts that legitimately allow zero matches.
set -f vs nullglob
set -f (also set -o noglob) disables pathname expansion entirely. That is different from nullglob, which keeps pathname expansion enabled but removes an unmatched pattern instead of leaving it literal.
Create a file and confirm normal globbing finds it:
touch matchme.txtWith globbing on, match* expands to the filename:
ls match*matchme.txtTurn globbing off with set -f:
set -fls now receives a literal match* argument:
ls match*ls: cannot access 'match*': No such file or directoryBash no longer expands match*; ls looks for a literal filename. Re-enable globbing:
set +fThe same pattern expands again:
ls match*matchme.txtnullglob and failglob assume globbing is still on. Use set -f when you need literal asterisks in every expansion, not when you only want to change no-match behavior.
Execution Options
lastpipe
By default, each stage in a pipeline runs in a subshell. A while read loop on the right side cannot update variables in the caller:
bash -c 'count=0; printf "a\nb\n" | while read -r line; do count=$((count+1)); done; echo count=$count'count=0With shopt -s lastpipe and job control disabled, Bash can run the final command of a foreground pipeline in the current shell:
bash -c 'shopt -s lastpipe; count=0; printf "a\nb\n" | while read -r line; do count=$((count+1)); done; echo count=$count'count=2lastpipe is easy to misconfigure in interactive shells because job-control state matters. Enable it only when you understand that constraint.
inherit_errexit
By default, Bash clears errexit inside command substitutions. shopt -s inherit_errexit changes that so $(…) inherits the caller's -e setting. Bash also enables inherit_errexit automatically in POSIX mode.
Without inherit_errexit, a failing command inside $(…) does not run under errexit in the substitution subshell:
bash -c 'set -e; f() { false; echo in_f; }; out=$(f); echo "out=$out parent continued"'out=in_f parent continuedThe substitution captured in_f even though false returned a non-zero status. The parent's own set -e did not react because the assignment completed successfully from its point of view.
Enable inherit_errexit and the chain changes: the substitution inherits -e, false terminates the substitution with a non-zero status, and the parent's set -e reacts to that failed assignment:
bash -c 'shopt -s inherit_errexit; set -e; f() { false; echo in_f; }; out=$(f); echo "out=$out parent continued"'The command exits with status 1 before printing the parent line. Full set -e rules belong in the Bash strict mode lesson; see Bash exit codes for how status values interact with && and ||.
Other Useful set Options
| Option | Command | Effect |
|---|---|---|
| errexit | set -e / set -o errexit |
Exit on most failing commands |
| nounset | set -u / set -o nounset |
Error on unset variables |
| pipefail | set -o pipefail |
Return the rightmost non-zero status if any pipeline command fails |
| xtrace | set -x / set -o xtrace |
Print commands before execution |
| noclobber | set -o noclobber |
Block > from overwriting existing files |
| noglob | set -f / set -o noglob |
Disable pathname expansion |
errexit, nounset, and pipefail form the core of strict mode. Enable them together only after you understand the exceptions in the strict-mode guide.
noclobber blocks > from replacing an existing regular file. Use >| when you intend to truncate anyway:
echo old > target.txtTurn noclobber on and try to replace the file with >:
set -o noclobberThe redirect should fail:
echo new > target.txtbash: target.txt: cannot overwrite existing fileTurn the option off when you are done:
set +o noclobberInteractive tracing with bash -x and PS4 customization belong in the debugging guide, not here.
Do Bash Options Affect Child Shells?
Shell options apply to the current Bash process. The main cases:
( … )subshells inherit the current settings, but changes remain inside the subshell.- A new
bashprocess normally starts with its own option state rather than simply inheriting every change made in the parent. - Exported
SHELLOPTSandBASHOPTScan pass enabled options to a child Bash process. - A sourced script runs in the current shell, so its option changes remain active.
GNU Bash documents exported SHELLOPTS and BASHOPTS as ways to pass enabled option sets to child Bash processes. Most scripts do not export these variables, so do not assume children pick up your interactive settings.
Sourced scripts leak option changes into the current shell:
cat > leak.sh <<'EOF'
shopt -s nullglob
EOFSource the file in the current shell:
source leak.shThe option stays enabled in this shell:
shopt -p nullglobshopt -s nullglobnullglob stayed on because sourcing executes the file in the current environment. Libraries that toggle options should restore the caller's state before returning.
Reset nullglob before continuing with the next example:
shopt -u nullglobTemporarily Change a Bash Option
When the work can run in a subshell, prefer a parenthesized block. Option changes inside ( … ) disappear when the block ends:
shopt -p nullglobEnable nullglob only inside the subshell:
(
shopt -s nullglob
files=( *.xyz )
printf 'match count: %d\n' "${#files[@]}"
)match count: 0After the block ends, the caller's setting is restored:
shopt -p nullglobshopt -u nullglobThat answers the common question: enable nullglob for one block without changing the caller's shell.
Explicit save/restore with shopt -p and eval is needed only when the option must change in the current shell and a subshell is not practical. Even then, guard against early returns and remember that shopt -p returns non-zero when the option is off, which can trip set -e scripts.
Bash set and shopt Quick Reference
| Goal | Command |
|---|---|
List set -o options |
set -o |
Print commands that restore set option state |
set +o |
List enabled set -o names |
echo "$SHELLOPTS" |
List shopt options |
shopt |
List enabled shopt names |
echo "$BASHOPTS" |
| Active short flags | echo "$-" |
| Stop on first failing command | set -e or set -o errexit |
| Rightmost non-zero pipeline status on failure | set -o pipefail |
| Empty glob expands to zero words | shopt -s nullglob |
| Unmatched glob is a hard error | shopt -s failglob |
| Last pipeline stage in current shell | shopt -s lastpipe (job control off) |
$(…) inherits caller's -e |
shopt -s inherit_errexit |
Block > overwrite |
set -o noclobber |
| Disable all globbing | set -f |
Test shopt option quietly |
shopt -q nullglob |
| Temporary option in a subshell | ( shopt -s nullglob; … ) |
Summary
Bash splits shell behavior between set and shopt. Use set for traditional shell attributes such as errexit, nounset, pipefail, and xtrace. Use shopt for additional optional behavior such as nullglob, failglob, and lastpipe. set -o and shopt list their respective option sets; shopt -o bridges to options both commands recognize.
Check enabled options with $SHELLOPTS, $BASHOPTS, and $-. Prefer a subshell when you need a temporary shopt change. Sourced scripts change the current shell's option state. Subshells inherit the current settings, but changes made inside them disappear when the subshell exits; a new Bash process normally starts with its own option state.
For pattern syntax, globstar, and extglob examples, use the globbing guide. For set -euo pipefail as a default script header, continue to Bash strict mode.
References
- GNU Bash Manual — The Set Builtin
- GNU Bash Manual — The Shopt Builtin
- GNU Bash Manual — Bash Variables
Frequently Asked Questions
Is shopt available in sh, or only in Bash?
shopt is a Bash builtin. POSIX sh and dash do not provide it. Scripts that must run under /bin/sh cannot rely on shopt options such as nullglob or failglob.
Are Bash shopt settings inherited by child shells?
Not automatically. A subshell ( … ) inherits the current option state, while a new Bash process normally has its own state. Exported BASHOPTS and SHELLOPTS can pass enabled options to child Bash processes.
What is the difference between set -o and shopt -o?
set -o is the traditional interface for shell attributes such as errexit, nounset, and pipefail. shopt -o restricts shopt to those same options, so shopt -o pipefail reports the same setting as set -o pipefail. Options such as nullglob exist only under shopt, not under set -o.

