Set Options

Luckily, the shell has a few basic features that give you debugging functionality beyond that of echo. The most basic of these are options to the set -o command (as covered in Chapter 3). These options can also be used on the command line when running a script, as Table 9-1 shows.

Table 9-1. Debugging options

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

set -o option

Command-line option

Action

noexec

-n

Don't run commands; check for syntax errors only

verbose

-v

Echo commands before running them

xtrace

-x

Echo commands after command-line processing

The verbose option simply echoes (to standard error) whatever input the shell gets. It is useful for finding the exact point at which a script is bombing. For example, assume your script looks like this:

alice
hatter
march
teatime
treacle
well

None of these commands is a standard UNIX program, and each does its work silently. Say the script crashes with a cryptic message like "segmentation violation." This tells you nothing about which command caused the error. If you type bash -v scriptname, you might see this:

alice
hatter
march
segmentation violation
teatime
treacle
well

Now you know that march is the probable culprit—though it is also possible that march bombed because of something it expected alice or hatter to do (e.g., create an input file) that they did incorrectly.

The xtrace option is more powerful: it echoes command lines after they have been through parameter substitution, command substitution, and the other steps of command-line processing (as listed in Chapter 7). For example:

.ps 8
$ set -o xtrace$ alice=girl+ alice=girl
$ echo "$alice"+ echo girl
girl
$ ls -l $(type -path vi)++ type -path vi
+ ls -F -l /usr/bin/vi
lrwxrwxrwx   1 root     root      5 Jul 26 20:59 /usr/bin/vi -> elvis*
$

As you can see, xtrace starts each line it prints with + (each + representing a level of expansion). This is actually customizable: it's the value of the built-in shell variable PS4. So if you set PS4 to "xtrace—>" (e.g., in your .bash_profile or .bashrc), then you'll get xtrace listings that look like this:

.ps 8
$ ls -l $(type -path vi)xxtrace--> type -path vi
xtrace--> ls -l /usr/bin/vi
lrwxrwxrwx   1 root     root      5 Jul 26 20:59 /usr/bin/vi -> elvis*
$

Notice that for multiple levels of expansion, only the first character of PS4 is printed. This makes the output more readable.

An even better way of customizing PS4 is to use a built-in variable we haven't seen yet: LINENO, which holds the number of the currently running line in a shell script.[2] Put this line in your .bash_profile or environment file:

PS4='line $LINENO: '

We use the same technique as we did with PS1 in Chapter 3: using single quotes to postpone the evaluation of the string until each time the shell prints the prompt. This will print messages of the form line N: in your trace output. You could even include the name of the shell script you're debugging in this prompt by using the positional parameter $0:

PS4='$0 line $LINENO: '

As another example, say you are trying to track down a bug in a script called alice that contains this code:

dbfmq=$1.fmq
...
fndrs=$(cut -f3 -d' ' $dfbmq)

You type alice teatime to run it in the normal way, and it hangs. Then you type bash -x alice teatime, and you see this:

+ dbfmq=teatime.fmq
...
+ + cut -f3 -d

It hangs again at this point. You notice that cut doesn't have a filename argument, which means that there must be something wrong with the variable dbfmq. But it has executed the assignment statement dbfmq=teatime.fmq properly... ah-hah! You made a typo in the variable name inside the command substitution construct.[3] You fix it, and the script works properly.

The last option is noexec, which reads in the shell script and checks for syntax errors, but doesn't execute anything. It's worth using if your script is syntactically complex (lots of loops, command blocks, string operators, etc.) and the bug has side effects (like creating a large file or hanging up the system).

You can turn on these options with set -o option in your shell scripts, and, as explained in Chapter 3, turn them off with set +o option. For example, if you're debugging a chunk of code, you can precede it with set -o xtrace to print out the executed commands, and end the chunk with set +o xtrace.

Note, however, that once you have turned noexec on, you won't be able to turn it off; a set +o noexec will never be executed.