Previous | Table of Contents | Next |
Most shell scripts that create temporary files use a trap command similar to the following:
trap "rm -f $TMPF; exit 2" 1 2 3 15
Here you remove the file stored in $TMPF and then exit with a return code of 2 indicating that exit was abnormal, when either a HUP, INT, QUIT, or TERM signal is received. Usually when a script exits normally its exit code is 0. If anything abnormal happens, the exit code should be nonzero.
Sometimes when more complicated clean up is required, a shell function can be used. In order to make the uu script (described in Chapter 12, Parameters) signal safe, you would add something similar to the following at the beginning of the script:
CleanUp() { if [ -f "$OUTFILE" ] ; then printf "Cleaning Up... "; rm -f "$OUTFILE" 2> /dev/null ; echo "Done." ; fi } trap CleanUp 1 2 3 15
Here the function CleanUp is invoked whenever the script receives a HUP, INT, QUIT, or TERM signal. This function removes the output file of the script, if that file exists. By cleaning up when a signal is received, partially encoded files are not left around to confuse users.
Note:
The main reason to use functions to handle signals is that it is nicer to have a shell function invoked when a signal is received rather than write in the appropriate code inline.Also, the commands that should be executed when a signal is received might be different depending on which point in the script the signal was received. In many cases it is difficult to capture that logic in a few commands, thus is it necessary to use a shell function as the signal handling routine.
In the previous examples, a single signal handler has been used for all signals. This is not required, and frequently different signals have different handlers. As an example, the following trap commands are completely valid:
trap CleanUp 2 15 trap Init 1
Here the script calls a clean up routine when an INT or TERM signal is received and calls its initialization routine when a SIGHUP is received. Declarations such as these are common in scripts that run as daemons.
For example, the following script, which is used to keep a process alive, behaves differently depending on the signal that it receives:
#!/bin/sh if [ $# -lt 1 ] ; then echo "USAGE: 'basename $0' command." Exit 0 fi Init() { printf "INFO: Initializing... " # check if the last backgrounded pid is valid, if it is # try an kill it. kill -0 $! 2> /dev/null; if [ $? -eq 0 ] ; then kill $! > /dev/null 2>&1 if [ $? -ne 0 ] ; then echo "ERROR: Already running as pid $!. Exiting." exit 1 fi fi # start a new program in the background $PROG & printf "Done.\n" } CleanUp() { kill -9 $! ; exit 2 ; } # main() trap CleanUp 2 3 15 trap Init 1 PROG=$1 Init while : ; do wait $! $PROG & done
Here you have two important functions, Init and CleanUp. The Init function is responsible for stopping any running instances of the program and then starting it again. The CleanUp function is responsible for killing the running program and then exiting.
All this script does is launch a program in the background and wait for it to finish. If the program finishes, it is launched again. The script exits when it receives an INT, QUIT, or TERM signal.
Sometimes there is no intelligent or easy way to clean up if a signal is received. In these cases, it is better to ignore signals than to deal with them. There are two methods of ignoring signals:
trap '' signals trap : signals
Here signals is a list of signals to ignore. The only difference between these two forms is that the first form passes a special argument, or null, to the trap command and the second uses the : command. The form to use is largely based on programmer style because both forms produce the same result.
If you simply wanted the uu script from Chapter 12 to ignore all signals, instead of cleaning up when it received a signal, you could add the following to the beginning of the script:
trap '' 1 2 3 15
Ignoring Signals During Critical Operations
When this command is given in a script, the script ignores all signals until it exits. From a programmers perspective, this might be a good idea, but from a users perspective, it is not. A better idea is to have only the critical sections of a script ignore traps. This is achieved by unsetting the signal handler when a section of critical code has finished executing.
As an illustration, say you have a shell script with a shell function called DoImportantStuff(). This routine should not be interrupted. In order to ensure this, you can install the signal handler before the function is called and reset it after the call finishes:
trap '' 1 2 3 15 DoImportantStuff trap 1 2 3 15
The second call to trap has only signal arguments. This causes trap to reset the handler for each of the signals to the default. By doing this, you enable the user to still terminate the script and ensure that critical operations are performed without interruption.
Previous | Table of Contents | Next |