Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Debugging Bash scripts is essential for maintaining reliable and predictable automation. Even the simplest script can fail if not thoroughly tested and debugged. Understanding common pitfalls—like improperly handled variables, syntax issues, or unexpected exit codes—can save time and frustration.
Effective debugging and troubleshooting is a critical skill for every Bash script author. By combining built-in features like set -x, trap, and PS4 with external tools such as shellcheck, you can quickly isolate and resolve script issues. Implementing robust error handling, thorough logging, and best practices like incremental development will further ensure that your scripts run smoothly and remain maintainable in the long term (which you’ll always be your number one goal: stability and performance!).
This guide is exclusively about debugging and finding problems in your Bash scripts. For more general guides about Bash scripting check these articles out:
set
Built-inBash provides several options with the set
command to manage error handling and debugging:
#!/usr/bin/env bash
set -euxo pipefail
#"e" = exit on error
#"u" = fail if uninitialized variable is used
#"x" = trace mode
#"o pipefail" = exit if any command in a pipeline fails
my_var="Hello"
echo $my_var
When you run the script above, Bash will show each command as it executes and exit immediately if any command fails.
You can also toggle debugging at specific parts of your script to minimize console noise:
#!/usr/bin/env bash
echo "Starting script…"
#Enable debugging
set -x
#Debugged section
ls -l /tmp
whoami
#Disable debugging
set +x
echo "Back to normal output."
trap
for Error HandlingYou can use trap to handle signals (e.g., SIGINT) or errors. This helps you run cleanup or logging commands before exiting.
#!/usr/bin/env bash
cleanup() {
echo "An error occurred. Exiting…"
# Additional cleanup or logging here
exit 1
}
Trap on any command error, SIGINT, or script exit
trap cleanup ERR INT
echo "Executing risky commands…"
#Commadn that might fail
false
echo "This line won't be reached if 'false' fails."
In the example above, the script calls the cleanup function if any command fails (ERR) or if the user presses Ctrl-C (INT).
Another way to manage errors is to test each command’s exit status:
command_output=$(some_command)
if [ $? -ne 0 ]; then
echo "some_command failed, got exit code $?"
exit 1
fi
While this approach is more verbose, it provides precise control over what happens after each command.
Redirecting script output and errors to a log file can help you analyze failures afterward.
#!/usr/bin/env bash
LOGFILE="/var/log/myscript.log"
{
echo "Script started at $(date)"
# Commands here
echo "Script completed."
} &>> "$LOGFILE"
Using &>>
sends both standard output and standard error to the LOGFILE.
On servers, you can use logger to send messages to syslog, enabling centralized logging.
logger "Script started."
#…
logger "Error encountered in script."
Customizing the PS4 variable can provide more informative debugging. For instance:
export PS4='+ ${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '
set -x
This prints the file name, line number, and function name before each traced command, making it easier to pinpoint the exact script location.
Shellcheck is a popular static analysis tool that scans your Bash script for potential issues such as syntax errors, deprecated features, and common pitfalls.
shellcheck myscript.sh
Shellcheck suggestions can help you fix problems before even running your script.
Process substitution can let you view the output of multiple commands side by side, which can be helpful when diagnosing issues:
diff <(some_command) <(some_command --debug)
Comparing the normal output to the debug output of the same command can highlight discrepancies.