Cheat sheets for various stuff
An attempt to bring order in good advice on writing Bash scripts I collected from several sources.
Always use long parameter notation when available. This makes the script more readable, especially for lesser known/used commands that you don’t remember all the options for.
# Avoid:
rm -rf -- "${dir}"
# Good:
rm --recursive --force -- "${dir}"
Don’t use:
cd "${foo}"
[...]
cd ..
but
(
cd "${foo}"
[...]
)
pushd
and popd
may also be useful:
pushd "${foo}"
[...]
popd
nohup foo | cat &
if foo
must be started from a terminal and run in the background.${var}
form (as opposed to $var
."${var}"
${ALL_CAPS}
${lower_case}
grep
ing to communicate status.$(cmd)
for command substitution (as opposed to backquotes)Prepend a command with \
to override alias/builtin lookup. E.g.:
$ \time bash -c "dnf list installed | wc -l"
5466
1.32user 0.12system 0:01.45elapsed 99%CPU (0avgtext+0avgdata 97596maxresident)k
0inputs+136outputs (0major+37743minor)pagefaults 0swaps
printf
is preferable to echo
. printf
gives more control over the output, it’s more portable and its behaviour is defined better.Print error messages on stderr. E.g., I use the following function:
error() {
printf "${red}!!! %s${reset}\\n" "${*}" 1>&2
}
Name heredoc tags with what they’re part of, like:
cat <<HELPMSG
usage $0 [OPTION]... [ARGUMENT]...
HELPMSG
Single-quote heredocs leading tag to prevent interpolation of text between them.
cat <<'MSG'
[...]
MSG
When combining a sudo
command with redirection, it’s important to realize that the root permissions only apply to the command, not to the part after the redirection operator. An example where a script needs to write to a file that’s only writeable as root:
# this won't work:
sudo printf "..." > /root/some_file
# this will:
printf "..." | sudo tee /root/some_file > /dev/null
Bash can be hard to read and interpret. Using functions can greatly improve readability. Principles from Clean Code apply here.
Declare variables with a meaningful name for positional parameters of functions
foo() {
local first_arg="${1}"
local second_arg="${2}"
[...]
}
Create functions with a meaningful name for complex tests
# Don't do this
if [ "$#" -ge "1" ] && [ "$1" = '-h' ] || [ "$1" = '--help' ] || [ "$1" = "-?" ]; then
usage
exit 0
fi
# Do this
help_wanted() {
[ "$#" -ge "1" ] && [ "$1" = '-h' ] || [ "$1" = '--help' ] || [ "$1" = "-?" ]
}
if help_wanted "$@"; then
usage
exit 0
fi
An idiom for tasks that need to be done before the script ends (e.g. removing temporary files, etc.). The exit status of the script is the status of the last statement before the finish
function.
finish() {
result=$?
# Your cleanup code here
exit ${result}
}
trap finish EXIT ERR
Source: Aaron Maxwell, How “Exit Traps” can make your Bash scripts way more robust and reliable.
Bash is not very easy to debug. There’s no built-in debugger like you have with other programming languages. By default, undefined variables are interpreted as empty strings, which can cause problems further down the line. A few tips that may help:
bash -n myscript.sh
Abort the script on errors and undbound variables. Put the following code at the beginning of each script.
set -o errexit # abort on nonzero exitstatus
set -o nounset # abort on unbound variable
set -o pipefail # don't hide errors within pipes
A shorter version is shown below, but writing it out makes the script more readable.
set -euo pipefail
bash -x myscript.sh
set -x
at the top of the scriptset -x
before and set +x
after the section.An annotated template for Bash shell scripts:
For now, see https://github.com/bertvv/dotfiles/blob/master/.vim/templates/sh