trap

We've been discussing how signals affect the casual user; now let's talk a bit about how shell programmers can use them. We won't go into too much depth about this, because it's really the domain of systems programmers.

We mentioned above that programs in general can be set up to Section 8.4 specific signals and process them in their own way. The trap built-in command lets you do this from within a shell script. trap is most important for "bullet-proofing" large shell programs so that they react appropriately to abnormal events—just as programs in any language should guard against invalid input. It's also important for certain systems programming tasks, as we'll see in the next chapter.

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

The syntax of trap is:

            trap cmd sig1 sig2 ...

That is, when any of sig1, sig2, etc., are received, run cmd, then resume execution. After cmd finishes, the script resumes execution just after the command that was interrupted.[12]

Of course, cmd can be a script or function. The sigs can be specified by name or by number. You can also invoke trap without arguments, in which case the shell will print a list of any traps that have been set, using symbolic names for the signals.

Here's a simple example that shows how trap works. Suppose we have a shell script called loop with this code:

while true; do
    sleep 60
done

This will just pause for 60 seconds (the sleep command) and repeat indefinitely. true is a "do-nothing" command whose exit status is always 0.[13] Try typing in this script. Invoke it, let it run for a little while, then type CTRL-C (assuming that is your interrupt key). It should stop, and you should get your shell prompt back.

Now insert this line at the beginning of the script:

trap "echo 'You hit control-C!'" INT

Invoke the script again. Now hit CTRL-C. The odds are overwhelming that you are interrupting the sleep command (as opposed to true). You should see the message "You hit control-C!", and the script will not stop running; instead, the sleep command will abort, and it will loop around and start another sleep. Hit CTRL-Z to get it to stop and then type kill %1.

Next, run the script in the background by typing loop &. Type kill %loop (i.e., send it the TERM signal); the script will terminate. Add TERM to the trap command, so that it looks like this:

trap "echo 'You hit control-C!'" INT TERM

Now repeat the process: run it in the background and type kill %loop. As before, you will see the message and the process will keep on running. Type kill -KILL %loop to stop it.

Notice that the message isn't really appropriate when you use kill. We'll change the script so it prints a better message in the kill case:

trap "echo 'You hit control-C!'" INT
trap "echo 'You tried to kill me!'" TERM
    
while true; do
    sleep 60
done

Now try it both ways: in the foreground with CTRL-C and in the background with kill. You'll see different messages.