Chapter 29. Debugging

Debugging is twice as hard as writing the code in the first
place. Therefore, if you write the code as cleverly as possible,
you are, by definition, not smart enough to debug it.

--Brian Kernighan

The Bash shell contains no built-in debugger, and only bare-bones
debugging-specific commands and constructs. Syntax errors or
outright typos in the script generate cryptic error messages that
are often of no help in debugging a non-functional script.

Try to find out what's wrong with Example 29-3
by uncommenting the echo "$badname" line. Echo
statements are useful for seeing whether what you expect is
actually what you get.

In this particular case, rm "$badname"
will not give the desired results because
$badname should not be quoted. Placing it
in quotes ensures that rm has only one
argument (it will match only one filename). A partial fix
is to remove to quotes from $badname and
to reset $IFS to contain only a newline,
IFS=$'\n'. However, there are simpler
ways of going about it.

Using the tee filter
to check processes or data flows at critical points.

Setting option flags -n -v -x

sh -n scriptname checks for
syntax errors without actually running the script. This is
the equivalent of inserting set -n or
set -o noexec into the script. Note
that certain types of syntax errors can slip past this
check.

sh -v scriptname echoes each
command before executing it. This is the equivalent of
inserting set -v or set
-o verbose in the script.

The -n and -v
flags work well together. sh -nv
scriptname gives a verbose syntax check.

sh -x scriptname echoes the result each
command, but in an abbreviated manner. This is the equivalent of
inserting set -x or
set -o xtrace in the script.

Inserting set -u or
set -o nounset in the script runs it, but
gives an unbound variable error message
at each attempt to use an undeclared variable.

Using an "assert" function to test a
variable or condition at critical points in a script. (This is
an idea borrowed from C.)

The exit command in a script
triggers a signal 0, terminating
the process, that is, the script itself.
[1]
It is often useful to trap the
exit, forcing a "printout"
of variables, for example. The trap
must be the first command in the script.

Trapping signals

trap

Specifies an action on receipt of a
signal; also useful for debugging.

A signal is a message
sent to a process, either by the kernel or another
process, telling it to take some specified action
(usually to terminate). For example, hitting a
Control-C
sends a user interrupt, an INT signal, to a running
program.

trap '' SIGNAL (two adjacent
apostrophes) disables SIGNAL for the remainder of the
script. trap SIGNAL restores
the functioning of SIGNAL once more. This is useful to
protect a critical portion of a script from an undesirable
interrupt.