| Article Index |
|---|
| Debugging in Shell script |
| Page 2 |
| Page 3 |
| All Pages |
Debugging in Shell script
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.
Example 29-1. A buggy script
#!/bin/bash |
Output from script:
./ex74.sh: [37: command not found |
Example 29-2. Missing keyword
#!/bin/bash |
Output from script:
missing-keyword.sh: line 10: syntax error: unexpected end of file |
Error messages may disregard comment lines in a script when reporting the line number of a syntax error.
What if the script executes, but does not work as expected? This is the all too familiar logic error.
Example 29-3. test24: another buggy script
#!/bin/bash |
Try to find out what's wrong with Example 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.
# Correct methods of deleting filenames containing spaces. |
Summarizing the symptoms of a buggy script,
It bombs with a "syntax error" message, or
It runs, but does not work as expected (logic error).
It runs, works as expected, but has nasty side effects (logic bomb).
Tools for debugging non-working scripts include
Inserting echo statements at critical points in the script to trace the variables, and otherwise give a snapshot of what is going on.

Even better is an echo that echoes only when debug is on.
### debecho (debug-echo), by Stefano Falsetto ###
### Will echo passed parameters only if DEBUG is set to a value. ###
debecho () {
if [ ! -z "$DEBUG" ]; then
echo "$1" >&2
# ^^^ to stderr
fi
}
DEBUG=on
Whatever=whatnot
debecho $Whatever # whatnot
DEBUG=
Whatever=notwhat
debecho $Whatever # (Will not echo.)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.)
Example 29-4. Testing a condition with an assert
#!/bin/bash
# assert.sh
#######################################################################
assert () # If condition false,
{ #+ exit from script
#+ with appropriate error message.
E_PARAM_ERR=98
E_ASSERT_FAILED=99
if [ -z "$2" ] # Not enough parameters passed
then #+ to assert() function.
return $E_PARAM_ERR # No damage done.
fi
lineno=$2
if [ ! $1 ]
then
echo "Assertion failed: \"$1\""
echo "File \"$0\", line $lineno" # Give name of file and line number.
exit $E_ASSERT_FAILED
# else
# return
# and continue executing the script.
fi
} # Insert a similar assert() function into a script you need to debug.
#######################################################################
a=5
b=4
condition="$a -lt $b" # Error message and exit from script.
# Try setting "condition" to something else
#+ and see what happens.
assert "$condition" $LINENO
# The remainder of the script executes only if the "assert" does not fail.
# Some commands.
# Some more commands . . .
echo "This statement echoes only if the \"assert\" does not fail."
# . . .
# More commands . . .
exit $?Using the $LINENO variable and the caller builtin.
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.




