Flags Example Flags Echo Example Echo Null Parameter Trap Null Parameter Example Error Messages Emergency Exit

Debugging

There are actually very few helpful things you can do to debug scripts. This however makes the task easier because there are less things to remember and because you soon learn to be very systematic in your approach. Firstly there are two flags you can set, either at the start of the script or part way through, which can give you information about what the shell thinks it is seeing. Then you can use the echo command to see what particular variables contain. Finally you can rely on the error messages the shell presents when it encounters a problem.

Flags:

The two flags are the -v and -x options for the shell. We have met these before in section Parameters, in passing, now lets see what they do and how to use them.

They can both be set on the command line, if required, by calling the script using a command line that looks something like:

/bin/sh -xv my_script arg_1 arg_2

A more usual approach is to put the options on the first line of the script file as was shown in Example Script 1.1 in Basic Shells. This will switch both options on for the whole script in either case. If this generates too much output, which is possible for long scripts, then you can be more selective by using the set command part way down the script. This example shows both options around a problem area:

example flags

echo "Start of Problem Area"
set -v on
set -x on
if [ -f $input_file ] && [ ! -f $output_file ]
then
    more=`wc -l $input_file | cut -c1-8`
    while $more
    do
	line=`head -$more $input_file | tail -1`
	echo "$more: $line"	>> $output_file
	more=`expr $more - 1`
    done
else
    echo "Error with files [$input_file] [ $output_file]"
    exit
fi
set -v off
set -x off
echo "End of Problem Area"

What this example will show is two things. Firstly, the -v will show every statement that the shell sees. It will echo the statements to the screen as the script runs. The -v option will also substitute the values of any variables for you so you can see the values being generated inside any if statements or other complex command constructs. Secondly, the -x option will indicate whether the script has executed the statement by putting a plus sign (+) in front of each statement processed as they are echoed to the screen. In the example, a plus sign would be inserted before the if statement, if it were true. If it were untrue, you would get a plus sign in front of the else statement. In this way you can monitor what values are being set and what statements are being processed during execution.

Another useful flag for debugging is the -n flag which causes the shell to parse the syntax structures without execution. It cannot do any damage to input or output files because nothing is being executed, but it will complain about syntax errors in your code and point out quote missmatches in the usual way, with cryptic messages about unexpected end of file. The lesser used flags are as follows:

  • The -u flag causes unset variables to be treated as an error condition. This is useful in detecting which variables managed to escape substitution and could point to the requirement of a default.
  • The -t flag exits after reading and executing one command.
  • The -e flag immediately exits if a command terminates with a non-zero exit status.
  • The -- flag instructs the shell not to change any flags.

There are one or two more which I have yet to find a real use for. Suggest you check your man pages and see if you can throw some light on them. You can also use the + instead of the - in front of any flag which has the effect of turning the flag off instead of on. Don't forget, the current flags can be found in the $- (dolla-hyphen) variable.

Echo:

Next in the debugging tool-set is the echo statement. The easiest you would think, but it is amazing how many times this one catches you out. Usually something is wrong with the script, you know about where the problem is and so you put in a few echoes to narrow down the area. Then when you re-run the script, either nothing is printed at all or the message "unexpected end of file" is printed. Very frustrating! What you have to watch here is the use of quotes and making sure that they balance. The key here is the message "unexpected end of file". It is usually accompanied by a reference to a line number which is one more than there are lines in the script. This is your clue. The message is telling you that you have opened a quote and the script interpreter has scanned the rest of the file and cannot find a matching closing quote. It could even be some way above where you think the problem is, where the opening quote you have highlighted may be acting as the closing quote for the real problem. The second clue for this is the fact that your echo has not printed out anything at all - the echo statement, itself enclosed by the bad quoting, has been hidden from the shell.

Once you have managed to get your echoes working, use them to check expected values before and after tests and in loops. Use the square bracket [ ] to enclose the variables, as shown in the numerous examples in this book. That way leading and training blanks will show up making it obvious why a test fails. Make use of the /usr/5bin/echo command, with its extra controls, to indicate the status of loops as shown in this example below. Here the echo is outputting a line of dots - one dot per loop cycle.

Example echo

while $more
do
	/usr/5bin/echo ".\c"			# Debug Dot
	line=`head -$more $input_file | tail -1`
	echo "$more: $line"			>> $output_file
	more=`expr $more - 1`
done

Null Parameter Trap:

Another useful feature is one we met earlier under Parameter Substitution. If your not sure if a parameter has been set and it is difficult to see - it might be set to a non-printing character for instance - then you can detect this by using one of the parameter substitution forms shown below:

Null Parameter Example

echo "Difficult Parameter = [${arg:?}]"

What this does is check whether the parameter has been set and returns either its value or the message "parameter null or not set". Try setting the parameter to <CTRL-G> and see what happens. The window will flash as the echo tries to display the character, but the square brackets show no character between them at all. Now set the parameter to the empty string and try again. This time the message is returned and the script exits, proving it has really been set to NULL.

Error Messages:

Get to know the error messages for your implementation. There is nothing wrong with generating error messages, just get to know what they mean. In this way, when they show up unexpectedly, you have a few clues about what is going wrong and how to fix it. There are a few key ones on my system which are good pointers. The messages themselves are usually very misleading, because the same messages are generally used for a whole range of errors relating to a syntactical element. But if you mess up enough you will eventually see which errors are responsible for particular messages. Here are some rules of thumb:

Message Meaning
unexpected end
of file at nnn
Bad quoting or complex command group
test expected No space around [ or ] or missing then
badly formed if Missing then or fi statements
missing test No do under while or for statements
syntax error Tried to compare alpha and number in test
invalid number A string in expr or bc contains alpha characters
bad number A real number used in expr (must be integer)

Start a file of all the messages you get from your shell. Against each message, put a list of all the errors that have generated that message and how you fixed the problem. You will find this most valuable and a great time saver.

Emergency Exit:

It you need to stop a script while it is running, use the <CTRL-C> character. If you have launched a script into background by appending the & symbol, then you will need to return it to foreground processing first, using the fg command, then use the <CTRL-C> character. If you have launched a script from the crontab and it is running away or stuck in an infinite loop, then you must locate its PID by using:

ps -ef | grep script_name

...then use...

kill -9 PID

...to kill the process. Also be on the look out for sub-processes that may have started in background (intentionally or otherwise!) from lists or sub-shells terminated by an ampersand (&). You may have to trace these back using the parent PID of the calling script, so remember to print it out or log it. The ps -ef command shows the current PID of all executing commands as well as the parent PID of all spawned processes.

Home Next Preface Introduction Basic Shells Shell Syntax Built-In Commands Command Substitution Startup & Environment Pipes, Lists & Redirection Input & Output Using Files Design Considerations Functions Debugging Putting It All Together Appendix Code Examples Page 213 This page was brought to you by rhreepe@injunea.demon.co.uk