Debugging shell scripts ~~~~~~~~~~~~~~~~~~~~~~~ Too many of you seem to make random changes to a script, run it, look at the output and make another change. This isn't productive -- it wastes your time. In what follows, it should go without saying that we have only one version of the script, that it lives in $HOME/bin, that we execute it by typing its name (only) and that we have it open in an editor window while we test it in another `xterm' window. Also, we have back-ups of the script in $HOME/bin/BU so that we can go back to the previous version simply by using `re'. ($HOME refers to your home directory.) There are two kinds of situations that need debugging: those with complex control logic (`if's, `while's, `test's, etc) and those which consist of a pipeline or a simple sequence of commands. Appropriate techniques for both kinds of situation are detailed below. Sequential Scripts ~~~~~~~~~~~~~~~~~~ The following techniques are handy: o Enable statement tracing as described below. Then, when you get a message about an `awk' or `sed' syntax error, you can see exactly which of your `awk' or `sed' commands was the one that caused the error. o Comment out lines of code with a `#' before the code. For example, this leaves temporary files behind so we can see them: # rm m1 m2 m3 m4 o Display temporary files, before deleting them. Then we won't have to `more' them one by one manually: pr -tm m1 m2 m3 m4 | more The advantage of `pr' is that it can do many thin files at once. o Leave the script early Turn this: ... ... ... < t1 > t2 ... ... ... < t2 > t1 ... ... ... < t1 > t2 into this: ... ... ... < t1 > t2 more t2 exit ... ... ... < t2 > t1 ... ... ... < t1 > t2 allowing us to see what was in "t2" before it got overwritten. o Use `more' and `exit' to see what is going down a pipe Turn this: ... ... ... | ... ... ... | ... ... ... | ... ... ... | into this: ... ... ... | ... ... ... | more exit ... ... ... | ... ... ... | When the lines before the `more' are debugged, we can move the extra lines down the script. o `echo' rather than do turn this: mv $file $newname OR wc < $input > $output into this: echo mv $file $newname OR echo "wc < $input > $output" to see what commands the script would have done. Note the weak quotes around the whole of the second line to show the variable values and avoid the input/output redirection. Complex Control Logic ~~~~~~~~~~~~~~~~~~~~~ Typically this kind of script crashes with: yourScript: test: argument expected or: yourScript: syntax error at line 6: `end of file' unexpected neither of which tell you where the error occurred. The first is often caused by an empty variable and the second is a missing or misplaced quotation mark. The thing to do is enable statement tracing by inserting this line: ~~~~~~~~~~~~~~~~~ set -x somewhere in the script. When the statement is executed, the Bourne shell starts echoing the script lines to standard output before executing them. For simple scripts, the line can be placed after the shebang line. For more complex one, it can be placed at the point at which you think tracing should be started. If needed, tracing can be stopped with: set +x Summary ~~~~~~~ All these techniques involve breaking problems into smaller ones. Smaller problems are easier to solve! Peter Thu Dec 18 11:30:41 GMT 2003