Whenever writing a shell script, I mostly always include this option at the beginning of the script
#!/usr/bin/env bash
set -Eeuo pipefail
#!/usr/bin/env bash
: trigger the command/usr/bin/env bash <script-path.sh>
.
The difference from using#!/bin/bash
is that we do not need to know the exact place ofbash
in the system (it may be/bin/bash
,/usr/bin/bash
, or/usr/local/bin/bash
, ...). Wrapping the command in anenv
command makes the script more portable to many environments.
Tip: if you need to add additional parameters to the invoked command, use the -S
(--split-string
) option of the env
command.
#!/usr/bin/env -S awk -f
The -S
option is not supported in every version of env
, but this shouldn't be your concern because it has been supported from long ago.
-E
or-o errtrace
: Allow error traps on function calls, subshell environment, and command substitutions.
Script:
#!/usr/bin/bash env
function a {
echo "a begins"
false
echo "a ends"
}
trap "echo \"ERR trap is triggered\"" ERR
echo "errtrace is on"
set -E
a
echo "errtrace is off"
set +E
a
Result:
errtrace is on
a begins
ERR trap is triggered
a ends
errtrace is off
a begins
a ends
-e
or-o errexit
: Exit immediately. When a command exits with a non-zero status, halt the script and exit with that status. By default, this option is off, bash continues the script even if an error occurs.
Script
#!/usr/bin/bash env
function a {
echo "a begins"
false
echo "a ends"
}
echo "errexit is off (default)"
set +e
a
echo "errexit is on"
set -e
a
Result
errexit is off (default)
a begins
a ends
errexit is on
a begins
-u
or-o nounset
: No unset. Prevent the usage of undefined variables. By default, this option is off, bash allows the usage of undefined variables.
Script
#!/usr/bin/bash env
echo "nounset is off"
set +u
echo "unknown var's value is $unknown_var"
echo "errexit is on"
set -u
echo "unknown var's value is $unknown_var"
Result
nounset is off
unknown var's value is
errexit is on
test.sh: line 9: unknown_var: unbound variable
-o pipefail
: pipe failure. If any command in the pipeline chain exits with a non-zero status, the whole command will exit with that status.
Script
#!/usr/bin/bash env
echo "pipefail is on"
set -o pipefail
false | true
echo $?
echo "pipefail is off"
set +o pipefail
false | true
echo $?
Result:
pipefail is on
1
pipefail is off
0
In addition, when you want to debug the script, -x
or -o xtrace
might be useful. This option tells bash to print all running commands.
These options can be turn off with set +<option name>
, like follows:
set +e
set +E
set +u
set +o pipefail
set +o errtrace
set +o errexit
set +o nounset
set +x
set +o xtrace
Besides, the following shopt
(shell option) options are also good to be considered in several cases.
shopt -s nullglob
: when a pattern have no match, expand them as null. By default, this option is not set and the pattern is expanded as a literal string.
Script
#!/usr/bin/bash env
cd "$(mktemp -d)"
shopt -s nullglob
echo a * b
ls * *.x
shopt -u nullglob
echo a * b
ls * *.x
cd -
Result
a b
a * b
ls: cannot access '*': No such file or directory
ls: cannot access '*.x': No such file or directory
/home/transang/tmp
shopt -s dotglob
: include files whose names start with a dot (.
) in the matching result.
Script
#!/usr/bin/bash env
cd "$(mktemp -d)"
shopt -s dotglob
touch .a
ls *
shopt -u dotglob
touch .a
ls *
rm .a
cd -
Result
.a
ls: cannot access '*': No such file or directory
/home/transang/tmp
To turn of an option with shopt
, use, for example shopt -u nullglob
.
Bash version used in the above sample scripts.
# bash --version
GNU bash, version 5.1.4(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Last but not least, if you are not familiar with writing bash scripts, I highly recommend using the popular shellcheck tool. Most popular editors have plugins for it.
Source: