Debugging Bash scripts automatically

Bug Hunting

Troubleshooting at the Command Line

Working in Eclipse is convenient but not always possible in practice (e.g., on a server without a graphical environment). Even though the Eclipse debugger is able to contact shell scripts running on external servers, it is not a very convenient approach. Bash Debugger is a better alternative; you can install this on your workstation by typing:

sudo apt-get install bashdb

After successfully completing the installation, you can instrument the execution of scripts by prefixing them with the bashdb command. To launch the script created above, which resides in the Eclipse workspace, enter:

tamhan@TAMHAN14:~/workspace$ bashdb ./
bash debugger, bashdb, release 4.2-0.8

The classic bashdb step-by-step debugger comes with more than two dozen commands. A complete list can be found on the corresponding SourceForge page [5]. However, the command line of the program is a bit confusing; for example, look at the following extract:

3: a=1234567890

For each line, the debugger informs you which script you are currently in and which statements the line contains. Below this is the bashdb prompt for the input of commands.

The value in the angle brackets, normally only a consecutive number, is bracketed multiple times if the program flow is nested deeper in stack frames.

If you want to walk through the code step by step, you can do so by entering next several times (Figure 4). Remember that bashdb does not terminate after program execution; you need to press q and Enter to exit the debugger mask.

Figure 4: When debugging in Eclipse, entering next takes you through the program step-by-step.

Step-by-Step with Exit Option

Developers brought up on the GNU Debugger know that step-by-step debuggers implement a comparatively complex command line, from which the user can also obtain information about the content of the currently loaded variable. The current content of a variable can be output, for example, with the examine command if you know the variable name:

bashdb<6> examine a
declare -- a="1234567890"

Bash scripts can be nested through functions and loops. The script below executes a wildcard calculation within a function:

function worker {
for i in `seq 1 10`;
echo "worker says: " $i
echo "go!"
echo "end!"

If you run this program in bashdb, you will notice that you only have to enter next three times. The entire body of worker is processed in one step. Control only returns in the echo "end!" line.

Special features of bashdb are the alternative step and finish commands. For lines like for i in `seq 1 10`; that comprise multiple statements, the step command tells the debugger to go one step deeper into your program.

Each line of the program is only accessed once, so for the loop, you toggle between the for and echo statements. If you get bored with this procedure at some point, you can tell bashdb to leave the current execution context by typing finish bashdb. However, this must be a file loaded into the script or a called function – at the moment, it is not possible to break out of a "classic" loop in this way.

Working with Breakpoints

Linear code execution will tend to become annoying if your shell script contains several hundred or even several thousand lines. Breakpoints let you work around this problem. Their behavior in bashdb is pretty much what you will be used to with debuggers for other programming languages.

Developers have two options at this point: You can create the desired breakpoints after starting the bashdb session, or you can use bashdb-trace. In the first option, the debugger recognizes various breakpoint declarations that let you address line numbers, files, and function names. This first example sets a breakpoint in the worker function:

9: echo "go!"
bashdb<0> break worker
Breakpoint 1 set in file /home/tamhan/workspace/, line 1.

The following three statements set breakpoints variously by line number:

break 28
Breakpoint 2 set in file, line 28.
Breakpoint 3 set in file, line 29.
break 28 if x==5
Breakpoint 4 set in file, line 28.

The first statement sets a breakpoint in the specified line of the currently opened file. The second statement demonstrates how to address "remote" files – unfortunately, it is not possible to specify function names here. The third statement shows a conditional breakpoint, which is only activated when its condition is met. In all cases, a program with breakpoints is run by entering cont:

bashdb<1> cont

After this command, the code runs until bashdb detects the activation of a breakpoint. At that point, you can either take action by typing next or start another pass by entering cont. If you are interested in specific changes to variables, you will want to use watchpoints instead, which you can set up with watch. The procedure is the same as for classic breakpoints.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus