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
Understand SignalsSignals 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
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
Catch SignalsLet'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
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
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
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
Add Multiple HandlersTo 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
Write a Complex HandlerIf 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
We issue the HUP signal by typing $ kill -HUP $(ps xww | awk '/\.\/signal-eg/{print $1}') Learn More
Trap over a Block of CodeSuppose 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
Learn More
|