Traps and signals in bash
Restoring an old post from an old blog about using traps and signals in bash.
Some issues when creating bash scripts are:
- Creating lock files to avoid things like multiple concurrent executions of the same script
- Cleanup temporary files if the script ends in some unexpected way
These probles can be solved using the built-in trap
. This command allows to capture and redefine the actions triggered when the script receives some signals.
Of course some signals cannot be redefined:
9) SIGKILL
18) SIGCONT
19) SIGSTOP
The complete list is available by running:
$ kill -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
16) SIGSTKFLT 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 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+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
Some signals are particularly interesting for our use cases like: SIGHUP
, SIGINT
, SIGQUIT
y SIGTERM
:
Signal | Description |
---|---|
SIGHUP | Históricamente era la señal que indicaba que el terminal al otro lado de la línea serie había “colgado”. Actualmente indica que el terminal controller se ha cerrado y suele redefinirse para recargar configuración y reabrir ficheros de log |
SIGINT | Interruption, typically Ctrl+C |
SIGQUIT | Enviado por el controlling terminal para terminar un programa (¿y generar un core dump? |
SIGTERM | Similar a SIGINT, indica la terminación de un programa |
These signals can be captured, redefined or ignored. For instance we can redefine the action that is executed when the script receives a Ctrl+C from the keyboard:
$ cat ctrlc.sh
#!/bin/bash
trap 'echo "I am ignoring you..."' SIGINT
while true; do
sleep 1
done
If we run the script and try to stop it with Ctrl+C this happens:
$ chmod 755 ctrlc.sh
$ ./ctrlc.sh
^I am ignoring you...
^I am ignoring you...
^I am ignoring you...
No you’ll have to find a different way to stop the program. This means that you have to be careful when redefining signals!
trap
command syntax is:
trap 'command list' SIGNAL1 [SIGNAL2 ...]
Signals can also be ignored:
trap '' SIGNAL
o reset to the default action as defined in signal.h
:
trap - SIGNAL
Enough theory, let’s start using it. Imagine we have a script that creates temporary files that we want to delete if our script exits in some unexpected way. We can redefine some signals:
#!/bin/bash
#
# Delete temp file:
trap '/bin/rm /tmp/mytemp.$$; exit' SIGHUP SIGINT SIGQUIT SIGTERM
To complete the picture we could add the pseudosignal ERR
. If ERR is in the signal list of the trap command, the defined command list will be executed if there is a non-zero exit code except if:
- The command that has failed is part of a list that follows a
while
oruntil
- It is part of a test in an
if
statement - It is part of a list of commands
&&
or||
- The output of the command is inverted via
!
So for example:
$ cat err.sh
#!/bin/bash -e
# Redefine signal
trap 'echo "An error has occurred"' ERR
touch testfile.txt
chmod 444 testfile.txt
echo "this is going to fail" >testfile.txt
echo "this will not be executed"
When the script runs this happens:
$ ./err.sh
./err.sh: line 7: testfile.txt: Permission denied
An error has occurred
Note that bash is executed with the -e
option (exit immediately if a pipeline returns a non-zero status) so the last line is not executed. This reminds me that someday I will have to write about bash modifiers, the great unknown 😄
There is another interesting pseudo-signal, EXIT
, which is triggered when the script is finished.
I planned to finish this post writing about the lock files, but it will have to wait for a second part.