Project86.Trap and Handle Unix Signals


Project 86. Trap and Handle Unix Signals

"How do I catch a signal sent to my Bash shell script?"

This project considers signals such as INT, HUP, and TERM, but from the receiving end. It shows how you might equip your Bash shell scripts with custom signal handlers (also called traps) to catch and handle signals sent to it. Project 40 considers signals from the other direction: how to issue them.

Learn More

Refer to Project 40, which lists the various signals and shows how you might send them to a running process by using the kill command.


Understand Signals

Signals are a feature built into Unix. A signal is like an interrupt: Sending a signal to a process causes the process to stop what it's doing and to respond. Signals are used to tell processes to take a specific action, such as restarting, terminating, or temporarily halting.

Tip

Signals are frequently used by faceless background programs (daemons) to receive instructions from the user: There's no other simple means to communicate with them. You may use the same technique with your own background scripts. Project 55 covers background jobs.


Try this example.

$ sleep 1000 wake up ok, you asked for it <Control-c>


A running process that's not responding to keyboard input (wake up typed in the example above) somehow manages to respond when you press Control-c. How does this happen? When any process is launched, it's accompanied by some special code that manages signals, called a handler. A handler is executed whenever a signal is sent to its process. Some processes supply their own handlers; other processes rely on default handlers Unix automatically attaches as the process is launched.

You may send a signal to a process by using the kill command. A limited number of signals may also be sent by pressing control sequences such as Control-c, which instructs Terminal to send the appropriate signal.

There are many signals, and individual processes can elect to respond to some signals and ignore others. Each process may respond to a signal in its own particular way.

Tip

List the current settings for Terminal, including keystroke-to-interrupt mappings, by typing

$ stty -e



Catch Signals

Let's write a short Bash shell script that demonstrates how to catch and handle a specific signal. If you don't supply your own handlers, a script is launched with a default set of handlers. We'll override the default handler for a signal called SIGINT (or just INT), which can be sent from the kill command or from Terminal by pressing Control-c.

Tip

Discover the signals that a command or daemon respects by checking its man page. Search for the section titled "SIGNALS."


Here's an example script that loops indefinitely, going dotty.

$ cat signal-eg #!/bin/bash trap 'echo "Got INT"' INT while true; do   echo -n "."; sleep 1 done


Learn More

Refer to Projects 9 and 10 for basic shell scripting.


Normally, we'd be able terminate the script by pressing Control-c in Terminal. The script catches the INT signal, however, by including the statement

trap 'echo "Got INT"' INT


This statement simply echoes the text "Got INT" and carries on regardless.

Let's run the script to see what happens when we press Control-c and when we send an INT signal from kill.

$ ./signal-eg ....^CGot INT          # <--here we typed Control-c (=INT) ......Got INT          # <--here we sent INT using command kill ......Terminated       # <--here we sent TERM using command kill $


We sent an INT signal by using the line

$ kill -INT $(ps xww | awk '/\.\/signal-eg/{print $1}')


Learn More

Project 40 shows you how to identify running processes and send signals to them.


To stop the script, we must send a stronger signal, such as TERM, by typing

$ kill -TERM $(ps xww | awk '/\.\/signal-eg/{print $1}')


To honor the interrupt signal and exit the script, we would change our trap statement to read

trap 'echo "Got INT, bye bye."; exit' INT $ ./signal-eg ....^C Got INT, bye bye.


Tip

TRap is a Bash built-in command. To learn more about it, and to display a list of signals, type

$ help trap $ trap -l



Add Multiple Handlers

To add more than one handler, simply add more trap statements. To handle both the INT and TERM signals, for example, we would write

trap 'echo "Got INT, bye bye."; exit' INT trap 'echo "Got TERM, bye bye."; exit' TERM


If more than one signal requires the same action, you may list them all in a single trap statement.

trap 'echo "Got INT or TERM, bye bye."; exit' INT TERM


Note

The signal KILL cannot be caught, because you can't override its default handler. This ensures that every process can be terminated by sending a KILL signal.


Write a Complex Handler

If your signal hander is more complex than just a couple of statements, consider having a trap statement call a function. Here's an example that traps the HUP signal and performs some significant processing upon its receipt.

$ cat signal-eg handlehup () {   echo "Reloading configuration"   # more statements here   echo "Restart complete" } trap 'handlehup' HUP while true; do   echo -n "."; sleep 1 done


When a HUP signal is received, the function handlehup is called.

$ ./signal-eg ...Reloading configuration # <-- we issued HUP using kill Restart complete .......


Tip

The HUP signal is often interpreted by daemons as a request to reload their configuration settings and restart.


We issue the HUP signal by typing

$ kill -HUP $(ps xww | awk '/\.\/signal-eg/{print $1}')


Learn More

Project 52 covers Bash functions.


Trap over a Block of Code

Suppose that you want to trap signals over a critical region of code but not over the whole script. This might be necessary in a script that writes information to a file in several steps, where an interrupt midway through the writing process would result in a half-written file.

We trap signals over the critical period, between opening and closing the file, by executing the critical region of code in a subshell and defining a handler that's local to the subshell. When the subshell completes, the handler ceases to be defined.

$ cat signal-eg #!/bin/bash # critical code - stop interrupts here (   trap 'echo "Caught by subshell"' INT   echo "Critical code"   a=100000; while ((a!=0)); do ((a--)); done ) # normal code - allow interrupts from now on echo "Normal code" a=100000; while ((a!=0)); do ((a--)); done


To illustrate this, both regions of code have a delay loop to make it possible to interrupt the script before it exits normally. After executing the script and pressing Control-c, we see that during execution of code in the critical region, the INT signal is caught and ignored. Thereafter, the INT signal terminates the script in the usual way.

$ ./signal-eg Critical code ^CCaught by subshell ^CCaught by subshell Normal code ^C $


Tip

Switch off a handler by specifying null code to the trap statement as a dash character.

trap - SIG


This on/off technique can be used to limit a trap to a block of code in preference to using a subshell.


Learn More

Project 85 covers subshells.





Mac OS X UNIX 101 Byte-Sized Projects
Mac OS X Unix 101 Byte-Sized Projects
ISBN: 0321374118
EAN: 2147483647
Year: 2003
Pages: 153
Authors: Adrian Mayo

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net