109

set and shopt are both shell builtins that control various options. I often forget which options are set by which command, and which option sets/unsets (set -o/+o, shopt -s/-u). Why are there two different commands that seemingly do the same thing (and have different arguments to do so)? Is there any easy way/mnemonic to remember which options go with which command?

5
  • 9
    Try looking at the second line of help set and help shopt to verify that even their authors think they do the same thing.
    – l0b0
    Feb 22, 2012 at 16:28
  • 3
    "Change the value of shell attributes" vs "Change the setting of each shell option".
    – Kevin
    Feb 22, 2012 at 16:30
  • 3
    In Bash 4.1.5(1)-release it says "Set or unset values of shell options and positional parameters." and "Set and unset shell options.", respectively.
    – l0b0
    Feb 22, 2012 at 16:33
  • Writing manpages makes you realize what you dont know and makes you try formulating things in a way you arent wrong about what you try writing about.
    – sjas
    Dec 22, 2018 at 13:59
  • In Bash version 5.0.17(1)-release, set is ambiguously described as both "Set or unset values of shell options and positional parameters." and then "Change the value of shell attributes and positional parameters, or display the names and values of shell variables."
    – The Quark
    Oct 18, 2021 at 20:47

7 Answers 7

63

As far as I know, the set -o options are the ones that are inherited from other Bourne-style shells (mostly ksh), and the shopt options are the ones that are specific to bash. There's no logic that I know of.

2
  • 1
    Any documentation which shows shopt is inherited ? Jul 3, 2015 at 1:13
  • 12
    Well, there are set -o options like posix/physical/interactive-comments that are not in ksh, and shopt ones that are in other shells including ksh for some like login_shell/nullglob. Like you say, there's no logic. It was probably the idea at the start (that SHELLOPTS would be the standard ones, and BASHOPTS the bash specific ones), but that got lost along the way, and now it just ends up being annoying and a UI design fiasco. Jan 25, 2018 at 13:05
41

The difference is in the changed environment variable used by bash. Setting with the set command results in $SHELLOPTS. Setting with the shopt command results in $BASHOPTS.

1
  • 22
    Ugh! That is even more confusing. My brain wants to associate shopt with $ SHELLOPTS rather than $ BASHOPTS. Mar 22, 2016 at 16:17
26

Probably linked to the history mentioned by @Gilles.

21

Easy but lost in history. The set command was used to modify the command line environment of the Bourne shell on Unix, /bin/sh. (Prior Unix shells that occupied the /bin/sh position did not have a set command at all.) Then as various Unix versions evolved, and new shell flavors were added, people realized that they needed to be able to change more (environment) things in order to keep shell scripting compatible. At that time Bash (the Bourne Again shell) got very popular and the additional shell options was needed, introducing shopt.

You can actually see these compatibility attempts in the shopt command.

$ shopt
autocd          off
cdable_vars     off
cdspell         off
checkhash       off
checkjobs       off
checkwinsize    off
cmdhist         on
compat31        off
compat32        off
compat40        off
compat41        off
compat42        off
complete_fullquote      on
direxpand       off
dirspell        off
dotglob         off
execfail        off
expand_aliases  on
extdebug        off
extglob         off
extquote        on
failglob        off
force_fignore   on
globstar        off
globasciiranges off
gnu_errfmt      off
histappend      on
histreedit      off
histverify      off
hostcomplete    on
huponexit       off
interactive_comments    on
lastpipe        off
lithist         off
login_shell     on
mailwarn        off
no_empty_cmd_completion off
nocaseglob      on
nocasematch     off
nullglob        off
progcomp        on
promptvars      on
restricted_shell        off
shift_verbose   off
sourcepath      on
xpg_echo        off

But not in the set command.

$ set -o
allexport       off
braceexpand     on
emacs           on
errexit         off
errtrace        off
functrace       off
hashall         on
histexpand      on
history         on
igncr           off
ignoreeof       off
interactive-comments    on
keyword         off
monitor         on
noclobber       off
noexec          off
noglob          off
nolog           off
notify          off
nounset         off
onecmd          off
physical        off
pipefail        off
posix           off
privileged      off
verbose         off
vi              off
xtrace          off

The set command has several functions, and does not just do one thing. The Bourne Again shell's shopt command in contrast only deals with shell options; one cannot accidentally mistype and end up setting a positional parameter or a shell variable instead. Other shells do not have a shopt command, but the Z shell similarly has setopt and unsetopt commands that only deal in shell options.

The aforegiven long-form options (to set -o) were also an innovation with respect to the Bourne shell that was added to the Bourne Again shell (and to others like the Z shell, Almquist shell, Korn shell, and Watanabe shell). The set -o mechanism with long-form options is in the Single Unix Specification, although it only has a few of the options that real shells actually have.

Long-form options won't be found in the descendants of the Bourne shell, such as the Heirloom Bourne shell, which commercial Unix vendors never adjusted to be POSIX-conformant. At the time, the C shell, and later the Korn shell, were seen as the places to go for innovative new features; and the Bourne shell largely ossified in the early 1980s.

As well as preventing typing mistakes from accidentally doing something else than setting shell options, another feature of the Bourne Again shell's shopt command is that it is more aligned with the "getopt" conventions that had been developed at the time, but that post-dated the Bourne shell. Unlike the set command with + and -, the only option-introducer character in the shopt command is -, and the distinction between setting and unsetting is -s versus -u.

Summary

  • set -o/set +o is the Single Unix Specification conformant command. Use it where that is important, albeit that you will also have to restrict yourself to a subset of what set -o/set +o is capable of.
  • shopt is the command that cannot accidentally be mistyped to do something other than affect shell options, and does only one thing, but is a Bashism. Use it if you prefer the modern conventional "getopt" syntax that took hold in later years. And remember that the fallback is to try with and without its -o option to pick the desired option set.
3
  • 3
    set as a way to set options wasn't in the original Unix shells, it was introduced by the Bourne shell in the late 70s. set -o name itself was added later by the Korn shell, specified but optional in POSIX, still not supported by "modern" versions of the Bourne shell like the /bin/sh of Solaris 10. Aug 23, 2016 at 14:00
  • I have learned something new. Thanks, archeology!:)
    – conny
    Feb 16, 2023 at 4:36
  • The best answer here, IMO
    – Pound Hash
    Jun 27, 2023 at 18:52
13

From the book "Linux Shell Scripting with Bash", p 63:

Historically, the set command was used to turn options on and off. As the number of options grew, set became more difficult to use because options are represented by single letter codes. As a result, Bash provides the shopt (shell option) command to turn options on and off by name instead of a letter. You can set certain options only by letter. Others are available only under the shopt command. This makes finding and setting a particular option a confusing task.

1
  • 5
    shouldn't shopt encompass all set varaibles? that way, new scripts can use shopt andset would be kept for compatibility.
    – ychaouche
    Jul 3, 2022 at 11:09
4

set originates from the bourne shell (sh) and is part of the POSIX standard, shopt is however not and is bourne-again shell (bash) specific:

0 sjas@ssg 14:31:45 ~  
set | grep -e SHELLOPTS -e BASHOPTS
BASHOPTS=checkwinsize:cmdhist:complete_fullquote:dotglob:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:progcomp:promptvars:sourcepath
SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor

0 sjas@ssg 14:31:51 ~  
shopt | column -t | grep -v off
checkwinsize             on
cmdhist                  on
complete_fullquote       on
dotglob                  on
expand_aliases           on
extglob                  on
extquote                 on
force_fignore            on
histappend               on
interactive_comments     on
progcomp                 on
promptvars               on
sourcepath               on

0 sjas@ssg 14:31:57 ~  
set -o | column -t | grep -v off
braceexpand           on
emacs                 on
hashall               on
histexpand            on
history               on
interactive-comments  on
monitor               on

0 sjas@ssg 14:37:41 ~ 
sh 

$ set -o
Current option settings
errexit         off
noglob          off
ignoreeof       off
interactive     on
monitor         on
noexec          off
stdin           on
xtrace          off
verbose         off
vi              off
emacs           off
noclobber       off
allexport       off
notify          off
nounset         off
priv            off
nolog           off
debug           off

$ shopt
sh: 3: shopt: not found

$ 
3

It looks like "set" options are inherited by subshells and shopts are not.

4
  • Nice catch. I wonder whether this is an intentional choice or a side-effect.
    – Kevin
    Jan 5, 2013 at 19:09
  • 2
    @user29778 At least under bash 4.1.5(1) the options set with set are not inherited by subshells.Both set and shopt options are not inherited by subshells.
    – Martin
    May 7, 2013 at 22:13
  • 1
    Can you point to the documentation that describes the inheritance characteristics of both set and shopt ? Jul 3, 2015 at 1:12
  • 11
    Both set -o and shopt options are inherited by subshells ((...), $(...), pipeline components). Whether they are inherited by other bash invocations depends on whether SHELLOPTS or BASHOPTS are in the environment or not. Aug 23, 2016 at 13:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .