"set -e" is short for "set -o errexit", that is, abort the script if a command returns with a non-zero exit code.
"set -u" is short for "set -o nounset", that is, abort the script if a variable name is dereferenced when the variable hasn't been set.
I also strongly suggest reading http://www.davidpashley.com/articles/writing-robust-shell-sc... for more things that you should be aware of when writing bash scripts.
> Errexit (a.k.a. set -e) is horrible, and you should not be using it in any new shell scripts you write. It exists solely for support of legacy scripts.
http://lists.gnu.org/archive/html/bug-bash/2017-03/msg00170....
edit: I asked after this (for whatever reason, my email isn't yet showing in the archives; maybe it will appear later), and got an interesting response!
http://lists.gnu.org/archive/html/bug-bash/2017-03/msg00171....
More specifically, every shell script should have "set -eu" at its second line, directly after the sh'bang line:
#!...
set -eu
For debugging or logging purposes, sometimes adding -v and/or -x comes handy: #!...
set -euvx #!/bin/sh -eu
Although i'm not sure if there are any advantages/disadvantagesDo you have a strong reason to prefer "set" over shebang flags? I have a slight preference for shebang flags so I can deliberately override them from the command line (but it's not a hill I'd die on):
$ cat foo
#!/bin/bash -eu
cd /nowhere
echo 'still here'
$ ./foo
./foo: line 2: cd: nowhere: No such file or directory
$ bash -c ./foo # thinking about system(3)
./foo: line 2: cd: nowhere: No such file or directory
$ bash +e ./foo # override
./foo: line 2: cd: nowhere: No such file or directory
still here
$
EDIT: Google's shell style guide has "Executables must start with #!/bin/bash and a minimum number of flags. Use set to set shell options so that calling your script as bash <script_name> does not break its functionality."https://google.github.io/styleguide/shell.xml?showone=Which_...
So the portable sh'bang line for bash is:
#!/usr/bin/env bash
And there you can't add "-eu" anymore, because only one argument is possible in sh'bang lines. (/usr/bin/env would try to find an executable named "bash -eu")So
#!/usr/bin/env bash
set -eu
is the more portable approach. However, for "/bin/sh" this should work portably: #!/bin/sh -euNitpick: on every BSD system I've used, it's /usr/local/bin/bash. But I definitely agree that using /usr/bin/env bash is the preferred solution for when you want to use bash (although I don't think it's even installed by default on most BSD's); as an Arch user (where /usr/bin/python is symlinked to python3 rather than python2), I appreciate those who go out of their way to use proper, portable shebangs.
set -euo pipefail $ set -eu
$ echo "$(false)"
$ #!/bin/bash
set -eu
export SHELLOPTS echo "$(false)"
swallows the exit code from $(false). $ cat /tmp/test.sh
set -euvx
export SHELLOPTS
echo "$(false)"
echo "echo of string with failed subcommand does not kill script"
$(false)
echo "but consuming exit code does"
$ /tmp/test.sh
export SHELLOPTS
+ export SHELLOPTS
echo "$(false)"
false
++ false
+ echo ''
echo "echo of string with failed subcommand does not kill script"
+ echo 'echo of string with failed subcommand does not kill script'
echo of string with failed subcommand does not kill script
$(false)
false
++ falseI mean, I can always just backtick pipe anything that would be convenient but avoid this nonsense.
Also -C so you don't accidentally clobber existing files. Useful to prevent symlink attacks, in addition to preventing stupid stuff.
My standard non-interactive shell script preamble begins with
set -e # strict error
set -u # don't expand unbound variable
set -f # disable pathname expansion
set -C # noclobber if [ "${1:-}" == "my awesome value" ]; then ...-eu should have been the default from the start, for one.
Hardcore mode.