You are writing a script that needs to be able to trap signals and respond accordingly.
Use the trap utility to set signal handlers.
First, use trap -l (or kill -l) to list the signals you may trap. They vary from system to system:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# NetBSD $ trap -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGEMT 8) SIGFPE 9) SIGKILL 10) SIGBUS 11) SIGSEGV 12) SIGSYS 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGURG 17) SIGSTOP 18) SIGTSTP 19) SIGCONT 20) SIGCHLD 21) SIGTTIN 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGINFO 30) SIGUSR1 31) SIGUSR2 32) SIGPWR # Linux $ trap -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 33) SIGRTMIN 34) SIGRTMIN+1 35) SIGRTMIN+2 36) SIGRTMIN+3 37) SIGRTMIN+4 38) SIGRTMIN+5 39) SIGRTMIN+6 40) SIGRTMIN+7 41) SIGRTMIN+8 42) SIGRTMIN+9 43) SIGRTMIN+10 44) SIGRTMIN+11 45) SIGRTMIN+12 46) SIGRTMIN+13 47) SIGRTMIN+14 48) SIGRTMIN+15 49) SIGRTMAX-15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX |
Next, set your trap(s) and signal handlers.
Note that the exit status of your script will be 128+signal number if the command was terminated by signal signal number.
Here is a simple case where we only care that we got a signal and don’t care what it was.
If our trap had been trap ” ABRT EXIT HUP INT TERM QUIT, this script would be rather hard to kill because any of those signals would just be ignored.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
$ cat hard_to_kill #!/bin/bash - trap ' echo "You got me! $?" ' ABRT EXIT HUP INT TERM QUIT trap ' echo "Later... $?"; exit ' USR1 sleep 120 $ ./hard_to_kill ^CYou got me! 130 You got me! 130 $ ./hard_to_kill & [1] 26354 $ kill -USR1 %1 User defined signal 1 Later... 158 You got me! 0 [1]+ Done ./hard_to_kill $ ./hard_to_kill & [1] 28180 $ kill %1 You got me! 0 [1]+ Terminated ./hard_to_kill |
This is a more interesting example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#!/usr/bin/env bash # cookbook filename: hard_to_kill function trapped { if [ "$1" = "USR1" ]; then echo "Got me with a $1 trap!" exit else echo "Received $1 trap--neener, neener" fi } trap "trapped ABRT" ABRT trap "trapped EXIT" EXIT trap "trapped HUP" HUP trap "trapped INT" INT trap "trapped KILL" KILL # This won't actually work trap "trapped QUIT" QUIT trap "trapped TERM" TERM trap "trapped USR1" USR1 # This one is special # Just hang out and do nothing, without introducing "third-party" # trap behavior, such as if we used 'sleep' while (( 1 )); do : # : is a NOOP done |
Here we invoke this example then try to kill it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
$ ./hard_to_kill ^CReceived INT trap--neener, neener ^CReceived INT trap--neener, neener ^CReceived INT trap--neener, neener ^Z [1]+ Stopped ./hard_to_kill $ kill -TERM %1 [1]+ Stopped ./hard_to_kill Received TERM trap--neener, neener $ jobs [1]+ Stopped ./hard_to_kill $ bg [1]+ ./hard_to_kill & $ jobs [1]+ Running ./hard_to_kill & $ kill -TERM %1 Received TERM trap--neener, neener $ kill -HUP %1 Received HUP trap--neener, neener $ kill -USR1 %1 Got me with a USR1 trap! Received EXIT trap--neener, neener [1]+ Done ./hard_to_kill |
First, we should mention that you can’t actually trap -SIGKILL (-9).
That signal kills processes dead immediately, so they have no chance to trap anything.
So maybe our examples weren’t really so hard to kill after all. But remember that this signal does
not allow the script or program to clean up or shut down gracefully at any time.
That’s often a bad thing, so try to avoid using kill -KILL unless you have no other choice.
Usage for trap is as follows:
1 |
trap [-lp] [arg] [signal [signal]] |
The first nonoption argument to trap is the code to execute when the given signal is received.
As shown above, that code can be self-contained, or a call to a function.
For most nontrivial uses a call to one or more error handling functions is probably best, since that lends itself well to cleanup and graceful termination features.
If this argument the null string, the given signal or signals will be ignored.
If the argument is – or missing, but one or more signals are listed, they will be reset to the shell
defaults. -l lists the signal names as show above, while -p will print any current traps and their handlers.
When using more than one trap handler, we recommend you take the extra time to alphabetize signal names because that makes them easier to read and find later on.
As noted above, the exit status of your script will be 128+signal number if the command was terminated by signal signal number.
There are three pseudosignals for various special purposes.
The DEBUG signal is similar to EXIT but is used before every command for debugging purposes.
The RETURN signal is triggered when execution resumes after a function or source (.) call.
And the ERR signal is triggered after a simple command fails.
Consult the bash Reference for more specific details and caveats, especially dealing with functions using the declare built-in or the set -o functrace option.