Bash set vs shopt: How Shell Options Work

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
Short on time?Beta Get the gist of this 12-minute read in seconds, then ask follow-up questions

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:

bash
set -o | grep pipefail
output
pipefail       	off

The same name is visible through the shopt bridge:

bash
shopt -o | grep pipefail
output
pipefail       	off

pipefail 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:

bash
shopt | grep nullglob
output
nullglob       	off

Confirm it does not appear under set -o:

bash
set -o | grep nullglob

The 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:

bash
set -- one two three

set -- clears the old positional list and assigns the new words. Read the middle argument:

bash
printf '%s\n' "$2"
output
two

That 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:

bash
set -o | head -5
output
allexport      	off
braceexpand    	on
emacs          	on
errexit        	off
errtrace       	off

The same state appears as replayable commands from set +o:

bash
set +o | head -5
output
set +o allexport
set -o braceexpand
set -o emacs
set +o errexit
set +o errtrace

List shopt options the same way:

bash
shopt | grep -E 'nullglob|failglob|lastpipe|inherit_errexit'
output
failglob        	off
inherit_errexit 	off
lastpipe        	off
nullglob        	off

Bash 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:

bash
echo "SHELLOPTS=$SHELLOPTS"
output
SHELLOPTS=braceexpand:hashall:interactive-comments

Enabled shopt options appear in $BASHOPTS:

bash
echo "BASHOPTS=$BASHOPTS"
output
BASHOPTS=checkwinsize:cmdhist:complete_fullquote:extquote:force_fignore:progcomp:sourcepath

Active single-character flags also appear in $-:

bash
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
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
bash -c 'set +e; false; echo survived'
output
survived

set +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:

bash
shopt -p nullglob failglob lastpipe inherit_errexit
output
shopt -u nullglob
shopt -u failglob
shopt -u lastpipe
shopt -u inherit_errexit

Use shopt -q in conditionals instead of parsing shopt display text:

bash
shopt -q nullglob; echo "nullglob off: exit $?"
output
nullglob off: exit 1

When the option is off, shopt -q returns exit status 1. Turn it on and the query succeeds:

bash
shopt -s nullglob; shopt -q nullglob; echo "nullglob on: exit $?"
output
nullglob on: exit 0

Reset nullglob before continuing:

bash
shopt -u nullglob

Note 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:

bash
printf '%q\n' *.xyz
output
\*.xyz

Bash passes the literal pattern *.xyz to printf because no file matched.

With nullglob, an unmatched pattern disappears from the argument list:

bash
shopt -s nullglob; printf '%q\n' *.xyz; shopt -u nullglob
output
''

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
bash -c 'shopt -s failglob; printf "%q\n" *.xyz'
output
bash: line 1: no match: *.xyz

The 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:

bash
touch matchme.txt

With globbing on, match* expands to the filename:

bash
ls match*
output
matchme.txt

Turn globbing off with set -f:

bash
set -f

ls now receives a literal match* argument:

bash
ls match*
output
ls: cannot access 'match*': No such file or directory

Bash no longer expands match*; ls looks for a literal filename. Re-enable globbing:

bash
set +f

The same pattern expands again:

bash
ls match*
output
matchme.txt

nullglob 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
bash -c 'count=0; printf "a\nb\n" | while read -r line; do count=$((count+1)); done; echo count=$count'
output
count=0

With shopt -s lastpipe and job control disabled, Bash can run the final command of a foreground pipeline in the current shell:

bash
bash -c 'shopt -s lastpipe; count=0; printf "a\nb\n" | while read -r line; do count=$((count+1)); done; echo count=$count'
output
count=2

lastpipe 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
bash -c 'set -e; f() { false; echo in_f; }; out=$(f); echo "out=$out parent continued"'
output
out=in_f parent continued

The 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
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:

bash
echo old > target.txt

Turn noclobber on and try to replace the file with >:

bash
set -o noclobber

The redirect should fail:

bash
echo new > target.txt
output
bash: target.txt: cannot overwrite existing file

Turn the option off when you are done:

bash
set +o noclobber

Interactive 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 bash process normally starts with its own option state rather than simply inheriting every change made in the parent.
  • Exported SHELLOPTS and BASHOPTS can 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:

bash
cat > leak.sh <<'EOF'
shopt -s nullglob
EOF

Source the file in the current shell:

bash
source leak.sh

The option stays enabled in this shell:

bash
shopt -p nullglob
output
shopt -s nullglob

nullglob 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:

bash
shopt -u nullglob

Temporarily Change a Bash Option

When the work can run in a subshell, prefer a parenthesized block. Option changes inside ( … ) disappear when the block ends:

bash
shopt -p nullglob

Enable nullglob only inside the subshell:

bash
(
  shopt -s nullglob
  files=( *.xyz )
  printf 'match count: %d\n' "${#files[@]}"
)
output
match count: 0

After the block ends, the caller's setting is restored:

bash
shopt -p nullglob
output
shopt -u nullglob

That 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


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.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)