In the following, the words "MUST" and "SHOULD" are to be interpreted as described in BCP 14 (Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14 (RFC 2119), March 1997).
bash
MUST be used as the shell scripting language throughout this
codebase.
Reasoning: The reasoning is two-fold:
- This choice is in accord with the Google Shell Style Guide (as of Revision 1.26).
bash
seems like the currently most popular shell scripting language: according to the TIOBE Index (as of 2019),bash
is the only shell scripting language among the top-50 programming languages.
All bash
executables MUST begin with the following shebang:
#!/usr/bin/env bash
This is used in place of the otherwise common #!/bin/bash
.
Reasoning: /bin
, by convention, is intended for essential
command-line binaries for the system administrator, while /usr/bin
is intended for non-essential command-line binaries for the casual
user. On some systems, bash
may not be regarded as an essential
command-line binary, and may therefore not reside under /bin
. By
using env
, we find bash
, wherever it may be, according to the
user's PATH
environment variable, and execute that.
After the shebang, all bash
scripts MUST either begin by going into
the following "strict
mode", or
comment on why the script is executed in a less strict mode:
set -euo pipefail
This does the following:
- The script fails as soon as any command fails (
-e
); the non-zero exit code gets propagated. - The script fails when an undefined variable is used (
-u
). - A pipeline of commands fails, if any intermediate command fails
(
-o pipefail
); the non-zero exit code gets propagated.
Reasoning: This makes shell scripts more robust and easier to debug. In particular, it reduces cascading errors, as long as we regard failing commands and use of non-declared variables as errors.
It appears that printf
has
a more standard behaviour across various shells (and versions of
bash
!) than echo
. Furthermore, and perhaps more importantly,
bash
-style echo
supports arguments, making it hard to echo
a
string like -n
. Hence, if you do not control the input passed to
echo
, better resort to printf
instead.
The following function hides the default echo
to implement the
behaviour you probably expect for echo
:
echo() (
IFS=" "
printf '%s\n' "$*"
)
Setting IFS to space has the effect that "$*"
is expanded
to
"$1 $2 $3..."
.
If you would like to avoid starting a subshell (note parentheses
around the body of the echo
function above), you can instead define
echo
as follows:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}