How to raise and take care of shell scripts: Debugging
How to debug a shell script
I don't know about a real shell debugger (offering breakpoints, step by step execution ...), but the shell knows a number of tools to make it easier to debug a script:
- set -n: Reads and interpretes all script lines but does not execute them. That's useful for a first syntax check. You should place this command at the top of your script, possibly you must repeat this inside of functions.
- set -x: Displays every command line the way it is executed. This should be your next debugging step, because the command lines are executed and that's why variables are assigned, exit codes are evaluated, ... Read the output CAREFULLY, check variable values and program output against your expectations - sometimes it's only a detail pointing to the error.
- trap: This shell builtin can - amongst other things - be used to control the script
behaviour in case of errors (the other things are described in Cleanup). That means you can
define a command to be executed when a non zero exit code is returned by a command - for instance to show variable's
trap "set" ERR
You also can define a command to be executed every time a script command line is processed:
trap "echo $LINENO $?" DEBUG
This will show you the current line number and the exit code of the last executed command. For details look at the bash manual pages (where else?). You can define the trap as often as you want and so specifically check the critical parts of your script.
- echo: Little, but very nice. Does exactly do what the name says: displays the arguments.
It's a simple way to find unset variables or to check potentially risky commands before they could damage your
echo "rm -rf /$DIR" is definitely the safe way instead of
rm -rf /$DIR if you forgot to set $DIR.
- read: This shell builtin waits for an input and that's why it's a way to stop the script
at a point of your choice. You can use this "interrupt" to look for files or directories the script should have created
at another terminal for instance. When working with the bash you additionally can take control of variables:
read -p "VAR=$VAR: " VAR displays the current variable's value and allows you to modify this value at runtime.
- exit: exit as a debug tool??? Yep - you can walk step by step
through your script by inserting this command before or after some critical points. It's mainly useful
in large scripts if you not at all have an idea of the error's cause. You work one's way toward the error. And when you
combine it with if-then-else constructs you are able to stop the script depending on various conditions:
test $? -eq 0 || exit terminates the script if the most recent command reported an error.
- redirect output: If you are running a more sophisticated script, it could be useful to
redirect the output and error messages into different files to read them calmly and in your own time:
./my_script >script.out 2>script.error.
- test data: Create a set of representative data prior to check your script against a "real world" sample. Your debugging sessions almost completely will fail if you try to find an error by analysing a 100,000 input line log. You will fall asleep before you have reached the end ;-) Try to find out the most of the possible input variants and check your script using these data sets. You'll see - when using high quality test data you'll find the most important 90% of script errors before "going live".