Unix: Debugging your scripts

All kinds of things can go wrong with your shell scripts, but they don't have to. Let's look a some debugging tips that can help ensure your scripts run the way you intend.

By  

The first step in debugging a script is to think through all the things that could go wrong when you're writing it. Think "OK, I'm about to read a file. But what if it doesn't exist? But what if the person running the script doesn't have read permission to the file?" If you anticipate all the things that can go wrong, your script can check for them, offer a polite, but to-the-point message whenever it runs into a problem and -- most importantly -- not do anything that will leave you in worse shape than before you ran the script (e.g., work partially and leave things in a state where you have to manually back out some changes before you can run the script again).

You can check not only the number of arguments that are passed to your script. You can check each one to make sure it matches the required criteria.

#!/bin/bash

if [ $# -ne 1 ]; then
    echo "USAGE: $0 file"
    exit 1
else
    if [ ! -f $1 ] || [ ! -r $1 ]; then
	echo "ERROR: $1 cannot be read or is not a file"
	exit 1
    fi
...

You should then try your script with invalid arguments and data. If your script requires teo arguments, pass it five and pass it none. Ask it to work with a file it can't read or run. Supply the arguments in the wrong order. Make sure it does the right thing no matter how you try to trip it up.

You could also decide to ignore any extra arguments that might be provided by checking only that $# is greater than or equal to 1.

When scripts are going to include fairly complex logic, I generally start by just mapping the flow -- the if's, the loops, the case statements and the functions -- and make sure the logic works properly by using echo commands before I start to plug in the commands that I want to run. This gives me confidence that I've mapped out the logic properly before I insert the commands that will do the heavy lifting.

#!/bin/bash

function chkAcct {
    echo running chkAcct with $1
}

for account in `grep ACCT $1 | awk -F: '{print $3}'`
do
    echo looking at $account
    chkAcct $account
done

Once I've seen that I'm looping through all the accounts included in the file used as my argument and seen that the function gets called, I can start plugging in the commands that are needed.

Another debugging technique is to plug in echo commands anywhere you want to see where you are. If your script fails with some kind of error, you might want to know if you're running into the problem on the 10th line of your script or the 89th. If you pepper your scripts with echo commands that tell you where you are (e.g., echo ready to collect stats), you will have a better idea where it's falling down.

The biggest problem with inserted echo commands is that you have to remove them once your debugging is done -- unless, of course, you don't. There are two ways to get around this.

For one, instead of inserting echo commands, you can turn on trace or debug mode by modifying the shebang line in your script. Once a -x (trace) or -v (verbose) is added to the end of your shebang line, your script will give you lots of output. It will show you as each command is run, so you will know exactly where you encountered errors and likely why. You can also do the same thing by calling your script like this:

$ bash -x myscript

If you use -x as an argument to bash this way, you won't have to go and rip the option back out of the script when you're done with your debugging.

Another option is to insert echo commands into your script that only run when you're working in debugging mode. You can do this by looking for an optional argument that turns on debugging mode and then using that to determine when your echo commands are run.

#!/bin/bash

if [ $# == 2 ] && [ $2 == "debug" ]; then
    echo debug mode is on
    debug="on"
else
    debug="off"
fi

if [ $# -ge 1 ] && [ -d $1 ]; then
    for log in `ls $1`
    do
        if [ $debug == "on" ]; then
            echo working on $log
        fi
        ./analog $log
    done
else
    echo "USAGE: $0 logdir"
    exit 1
fi

If "debug" isn't provided as the second argument, any echo commands couched within if/then logic will not be displayed. This logic might help you resolve problems you have with the script now or a year from now.

Another option, and one I like even better, is to write your scripts to respond to a debug variable if it is set and to run normally if it is not. You can use your debug variable to determine when output is provided, but set it not within your script, but within your shell.

Here, we see an example of using $debug to determine when a "working on $log" message is displayed. To use debug mode in this case, you just export debug as a variable before you call the script. For example: $ export debug=1

#!/bin/bash

if [ $# -ge 1 ] && [ -d $1 ]; then
    for log in `ls $1`
    do
        if [ $debug ]; then
            echo working on $log
        fi
        ./analog $log
    done
else
    echo "USAGE: $0 logdir"
    exit 1
fi

I like not having to go back and edit out echo statements when I've finished my troubleshooting. I like to leave them in scripts that are particularly complicated, especially if I might have to go back in another year or two and make changes or someone else is likely to inherit responsibility for them.

The last bit of advice that I have for debugging is that you want to work with real data, but not necessarily complete data. I have some scripts which literally process millions of records. When I'm in debug mode, I don't want to have to run through 20 million records before I find out if my script is doing the right thing and I certainly don't want to look at any kind of output 20 million times. Starting with a sampling of records -- maybe 10 or 1,000 -- will help verify that my script is processing records correctly before I'm willing to let it rip on the 20 million of them.

Photo Credit: 

flickr / DrPhotoMoto

Join us:
Facebook

Twitter

Pinterest

Tumblr

LinkedIn

Google+

Answers - Powered by ITworld

Join us:
Facebook

Twitter

Pinterest

Tumblr

LinkedIn

Google+

Ask a Question
randomness