How to Debug Bash Scripts
Bash is the default scripting language in most Linux systems. Its usage ranges from an interactive command interpreter to a scripting language for writing complex programs. Debugging facilities are a standard feature of compilers and interpreters, and bash is no different in this regard. In this article, I will explain various techniques and tips for debugging Bash scripts.
Tracing script execution
You can instruct Bash to print debugging output as it interprets you scripts. When running in this mode, Bash prints commands and their arguments before they are executed.
To see how this works, let's try it on an example script. The following simple script greets the user and prints the current date:
#!/bin/bash echo "Hello $USER," echo "Today is $(date +'%Y-%m-%d')"
To trace the execution of the script, use bash -x to run it:
$ bash -x example_script.sh + echo 'Hello ayman,' Hello ayman, ++ date +%Y-%m-%d + echo 'Today is 2009-08-24' Today is 2009-08-24
In this mode, Bash prints each command (with its expanded arguments) before executing it. Debugging output is prefixed with a number of + signs to indicate nesting. This output helps you see exactly what the script is doing, and understand why it is not behaving as expected.
Adding line numbers to tracing output
In large scripts, it may be helpful to prefix this debugging output with the script name, line number and function name. You can do this by setting the following environment variable:
export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: '
Let's trace our example script again to see the new debugging output:
$ bash -x example_script.sh +example_script.sh:2:: echo 'Hello ayman,' Hello ayman, ++example_script.sh:3:: date +%Y-%m-%d +example_script.sh:3:: echo 'Today is 2009-08-24' Today is 2009-08-24
Tracing part of a script
Sometimes, you are only interested in tracing one part of your script. This can be done by calling set -x where you want to enable tracing, and calling set +x to disable it. Let's apply this to our example script:
#!/bin/bash echo "Hello $USER," set -x echo "Today is $(date %Y-%m-%d)" set +x
Now, let's run the script:
$ ./example_script.sh Hello ayman, ++example_script.sh:4:: date +%Y-%m-%d +example_script.sh:4:: echo 'Today is 2009-08-24' Today is 2009-08-24 +example_script.sh:5:: set +x
Notice that we no longer need to run the script with bash -x.
Logging
Tracing script execution is sometimes too verbose, especially if you are only interested in a limited number of events, like calling a certain function or entering a certain loop. In this case, it's better to log the events you are interested in. Logging can be achieved with something as simple as a function that prints a string to stderr:
_log() { if [ "$_DEBUG" == "true" ]; then echo 1>&2 "$@" fi }
Now you can embed logging messages into your script by calling this function:
_log "Copying files..." cp src/* dst/
Log messages are printed only if the _DEBUG variable is set to true. This allows you to toggle the printing of log messages depending on your needs. You don't need to modify your script in order to change this variable; you can set it on the command line:
$ _DEBUG=true ./example_script.sh
Using the Bash debugger
If you are writing a complex script and you need a full-fledged debugger to debug it, then you can use bashdb, the Bash debugger. The debugger contains all the features that you would expect, like breakpoints, stepping in and out of functions, and attaching to running scripts. Its interface is a bit similar to gdb. You can read the documentation of bashdb for more information.













Dennis Roberts (not verified) | Great article | Tue, 2009/08/25 - 5:20am
I liked your write up. I have been using Linux for the last 10 years and not a day goes by that I don't learn something new. The PS4 is new to me. I also like your log function.
Ayman | Thanks for your comment. Glad | Tue, 2009/08/25 - 7:09pm
Thanks for your comment. Glad to know that you found it useful.
Anonymous (not verified) | Hello ayman, | Tue, 2009/08/25 - 8:30am
Today is 2009-08-25
Anonymous (not verified) | Excellent - thanks! - Andre. | Tue, 2009/08/25 - 9:19am
Excellent - thanks!
- Andre.
Abdul Fattah (not verified) | Thanks Ayman, very useful | Tue, 2009/08/25 - 10:48am
Thanks Ayman, very useful tips :)
Anonymous (not verified) | I'd highly recommend all your | Tue, 2009/08/25 - 11:12am
I'd highly recommend all your bash scripts start off by:
set -eThis will abort the rest of the script if any of the commands you run return non-0. This will help find and avoid what would have otherwise been silent lurking problems.
decasm (not verified) | Logging with log4sh | Tue, 2009/08/25 - 1:19pm
The logging function you have there is good, but for larger projects, log4sh might be best.
Drew (not verified) | Vivid articles. thinks | Tue, 2009/08/25 - 2:08pm
Vivid articles.
thinks
Dunk (not verified) | How about | Tue, 2009/08/25 - 5:13pm
set -o xtrace
Ayman | "set -o x" is the short form | Tue, 2009/08/25 - 7:11pm
"
set -o x" is the short form of "set -o xtrace".Marko (not verified) | Hey Ayman, thanks a lot! I'm | Tue, 2009/08/25 - 6:17pm
Hey Ayman, thanks a lot! I'm still fairly new to bash scripting and there some really nice tips here...
omid8bimo (not verified) | cool notes dude :) pretty | Tue, 2009/08/25 - 6:57pm
cool notes dude :)
pretty useful
Thell (not verified) | Thank you, thank you, thank you. | Thu, 2009/09/03 - 4:47pm
Your sharing this info is greatly appreciated.
That PS4 was just what the doctor ordered.
It has been added to:: http://mywiki.wooledge.org/BashGuide/Practices/Debugging
missnoerrors (not verified) | Useful and concise info and | Sat, 2009/10/03 - 4:58am
Useful and concise info and comments. Thanks!
set -e is what I was looking for to control build script, now it'll get a logging upgrade too!
Polprav (not verified) | No teme | Tue, 2009/10/20 - 2:06pm
Hello from Russia!
Can I quote a post in your blog with the link to you?
Post new comment