Tags: #shell #bash
[!NOTE] Source https://sharats.me/posts/shell-script-best-practices/
This article is about a few quick thumb rules I use when writing shell scripts that I’ve come to appreciate over the years. Very opinionated.
Use bash
. Using zsh
or fish
or any other, will make it hard for others to understand / collaborate. Among all shells, bash
strikes a good balance between portability and DX.
Just make the first line be #!/usr/bin/env bash
, even if you don’t give executable permission to the script file.
Use the .sh
(or .bash
) extension for your file. It may be fancy to not have an extension for your script, but unless your case explicitly depends on it, you’re probably just trying to do clever stuff. Clever stuff are hard to understand.
Use set -o errexit
at the start of your script.
bash
exits instead of continuing with the rest of the script.Prefer to use set -o nounset
. You may have a good excuse to not do this, but, my opinion, it’s best to always set it.
"${VARNAME-}"
instead of "$VARNAME"
, and you’re good.Use set -o pipefail
. Again, you may have good reasons to not do this, but I’d recommend to always set it.
Use set -o xtrace
, with a check on $TRACE
env variable.
if [[ "${TRACE-0}" == "1" ]]; then set -o xtrace; fi
.TRACE=1 ./script.sh
instead of ./script.sh
.Use [[ ]]
for conditions in if
/ while
statements, instead of [ ]
or test
.
[[ ]]
is a bash builtin keyword, and is more powerful than [ ]
or test
.Always quote variable accesses with double-quotes.
[[ ]]
condition. But even there I’d recommend quoting.bash
arrays will likely serve you much better.Use local
variables in functions.
Accept multiple ways that users can ask for help and respond in kind.
-h
or --help
or help
or just h
or even -help
, and in all these cases, print help text and exit.When printing error messages, please redirect to stderr.
echo 'Something unexpected happened' >&2
for this.Use long options, where possible (like --silent
instead of -s
). These serve to document your commands explicitly.
If appropriate, change to the script’s directory close to the start of the script.
cd "$(dirname "$0")"
, which works in most cases.Use shellcheck
. Heed its warnings.
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
if [[ "${TRACE-0}" == "1" ]]; then
set -o xtrace
fi
if [[ "${1-}" =~ ^-*h(elp)?$ ]]; then
echo 'Usage: ./script.sh arg-one arg-two
This is an awesome bash script to make your life better.
'
exit
fi
cd "$(dirname "$0")"
main() {
echo do awesome stuff
}
main "$@"
I try to follow these rules in my scripts, and they’re known to have made at least my own life better. I’m still not consistent though, unfortunately, in following my own rules. So perhaps writing them down this way will help me improve there as well.
Do you have anything you think I should add to this? Please share in the comments!
Edit 1: Included fixes from HN comments at https://news.ycombinator.com/item?id=33355407 and https://news.ycombinator.com/item?id=33355077.
Edit 2: Fix from https://news.ycombinator.com/item?id=33354759.