If you're running a shell script and you press your interrupt key (Section 5.8) (like CTRL-c), the shell quits right away. That can be a problem if you use temporary files in your script, because the sudden exit might leave the temporary files there. The trap command lets you tell the shell what to do before it exits. A trap can be used for a normal exit, too. See Table 35-1.
Here's
a script named zmore that uses a temporary
file named /tmp/zmore$$ in a system
temporary-file directory. The shell will replace $$
with its process ID number
(Section 24.3). Because no other
process will have the same ID number, that file should have a unique name. The
script uncompresses (Section 15.6) the file named on its
command line, then starts the more file
viewer.[4] The script uses traps, so it will
clean up the temporary files, even if the user presses
CTRL-c. The script also sets a default exit status of 1 that's reset to 0 if
more quits on its own (without an
interrupt). If you are on a Linux system, you may find that gzcat is simply named zcat.
#!/bin/sh # zmore - UNCOMPRESS FILE, DISPLAY WITH more # Usage: zmore file stat=1 # DEFAULT EXIT STATUS; RESET TO 0 BEFORE NORMAL EXIT temp=/tmp/zmore$$ trap 'rm -f $temp; exit $stat' 0 trap 'echo "`basename $0`: Ouch! Quitting early." 1>&2' 1 2 15 case $# in 1) gzcat "$1" >$temp more $temp stat=0 ;; *) echo "Usage: `basename $0` filename" 1>&2 ;; esac
There are two traps in the script:
The first trap, ending with the number 0
, is executed for all shell exits — normal or
interrupted. It runs the command line between the single quotes. In this
example, there are two commands separated with a semicolon (;) (Section 28.16). The first command removes the temporary file
(using the
-f option (Section 14.10), so rm won't give an error message if the file
doesn't exist yet). The second command exits with the value stored in
the stat shell variable. Look ahead at the rest of
the script —
$stat
will always be 1
unless the more command quit on its
own, in which case stat will be reset to 0.
Therefore, this shell script will always return the right exit status —
if it's interrupted before it finishes, it'll return 1; otherwise,
0.[5]
The second trap has the numbers
1
2
15
at the end. These are signal
numbers that correspond to different kinds of interrupts. On newer
shells, you can use signal names instead of the numbers. There's a short
list in Table 35-1. For a
list of all signals, type kill -l
(lowercase "L") or see your online
signal(3) or signal(2)
manual page. Alternatively, look for a file named /usr/include/signal.h or /usr/include/linux/signal.h (which itself
just includes /usr/include/asm/signal.h, which is where the constants
themselves are defined).
Shell
scripts don't always have two traps. Look at
the nom (Section 33.8) script for an example.
I usually don't trap signal 3 (QUIT) in scripts that I use myself. That gives
me an easy way to abort the script without springing the trap (removing
temporary files, etc.). In scripts for general use, though, I usually do trap
it.
Also, notice that the echo commands in the script have 1>&2 (Section 36.16) at the end. This is the standard way to make error
messages. In this particular script, that doesn't matter much because the script
is used interactively. But it's a good habit to get into for all of your
scripts.
If your trap runs a series of commands,
it's probably neater to call a shell function
(Section 29.11) than a list of
commands:
trap funcname
1 2 15
—JP and SJC
[4] The script could run gzcat $1 |
more
directly, but some versions of more can't back up when reading from a
pipe. You may prefer to use less, at
any rate.
[5] It's important to use single quotes rather than double quotes
around the trap. That way,
the value of $stat
won't be
interpreted until the trap is actually executed when the script
exits.