An attempt to bring order in good advice on writing Bash scripts I collected from several sources.
- VSCode with shellcheck and Bash IDE extensions.
- WSL or WSL2 on Windows 10/11 environments.
The principles of Clean Code apply to Bash as well. 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.
rm -rf -- "${dir}"
rm --recursive --force -- "${dir}"
rm --recursive --force -- "/{dir}" #If $dir is empty, it will delete everything!
rm --recursive --force -- "/${dir:?}" #If $dir is empty, the command will fail
cd "${foo}" [...] cd ..
( cd "${foo}" [...] )
- Prefer local variables within functions over global variables.
- If you need global variables, make them read-only.
- Variables should always be referred to in the ${var} form (as opposed to $var).
- Variables should always be quoted, especially if their value may contain a whitespace or separator character: "${var}".
- Capitalization
Environment (exported) variables:
${ALL_CAPS} Local variables: $ {lower_case} - Positional parameters of the script should be checked, those of functions should not.
- Some loops happen in subprocesses, so don’t be surprised when setting variabless does nothing after them. Use stdout and greping to communicate status.
Good: "$my_var" Bad: $my_var
Good: "$(cmd)" Bad: $(cmd)
There are exceptions where quoting is not necessary, but because it never hurts to quote.
The exceptions only matter in discussions of style – feel welcome to ignore them. For the sake of style neutrality, Shellharden does honor a few exceptions:
- variables of invariably numeric content:
$?, $ $,$!, $ # and array length ${#array[@]} - assignments: a=$b
- the magical case command: case $var in … esac
- the magical context between double-brackets ([[ and ]]) – this is a language of its own.
Should I use backticks? Command substitutions also come in this form:
Correct: "cmd
"
Bad: cmd
my_array=()
my_array=(1 2 3)
${my_array[2]}
${my_array[@]}
${!my_array[@]}
${#my_array[@]}
my_array[0]=3
my_array+=(4)
my_array=( $(ls) )
${my_array[@]:s:n}
unset my_array[2]
for element in "${my_array[@]}"; do echo "${element}" done
if [[ ${#my_array[@]} -eq 0 ]]; then echo "Array is empty" fi
- https://github.com/dylanaraps/pure-bash-bible
- https://bertvv.github.io/cheat-sheets/Bash.html
- https://github.com/anordal/shellharden/blob/master/how_to_do_things_safely_in_bash.md
- https://stackoverflow.com/questions/10953833/passing-multiple-distinct-arrays-to-a-shell-function
- https://www.gauchocode.com/mastering-bash-i-our-first-script/