To write robust Bash scripts, I recommend you to:
1. Activate the Bash options: errexit, nounset
Add these two options at the top of your BASH scripts:
|
#!/usr/bin/env bash set -o errexit set -o nounset |
- errexit will stop your Bash script each time a command in your script returns an exit code different than “0”. It does not show any message when it quits, this is why it is important to show a traceback (check the second advice below “show a Bash traceback”).
- nounset will stop your script and output an error if you attempt to use undefined variables. nounset is very important because it can avoid you to run dangerous commands like: rm –fr “/$undefinedvariable”
2. Show a Bash Traceback
Because the option “errexit” does not show any message when it stops your Bash script in some cases (for example var=$(yourcommand) will exit without any message, even when yourcommand returns an exit code different than zero), I recommend you to add the code below to show a traceback each time “errexit” forces your Bash script to stop:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
#!/usr/bin/env bash set -o errexit # stop the script each time a command fails set -o nounset # stop if you attempt to use an undef variable function bash_traceback() { local lasterr="$?" set +o xtrace local code="-1" local bash_command=${BASH_COMMAND} echo "Error in ${BASH_SOURCE[1]}:${BASH_LINENO[0]} ('$bash_command' exited with status $lasterr)" if [ ${#FUNCNAME[@]} -gt 2 ]; then # Print out the stack trace described by $function_stack echo "Traceback of ${BASH_SOURCE[1]} (most recent call last):" for ((i=0; i < ${#FUNCNAME[@]} - 1; i++)); do local funcname="${FUNCNAME[$i]}" [ "$i" -eq "0" ] && funcname=$bash_command echo -e " $i: ${BASH_SOURCE[$i+1]}:${BASH_LINENO[$i]}\t$funcname" done fi echo "Exiting with status ${code}" exit "${code}" } # provide an error handler whenever a command exits nonzero trap 'bash_traceback' ERR # propagate ERR trap handler functions, expansions and subshells set -o errtrace |
The traceback will help you to know exactly which command in your Bash script exited with an error code different than “0”:
|
Error in ./test.sh:43 ('false' exited with status 1) Traceback of ./test.sh (most recent call last): 0: ./test.sh:43 false 1: ./test.sh:49 main Exiting with status -1 |
3. Check your Bash scripts with shellcheck and follow the recommendations of Advanced Bash-Scripting Guide.