Skip to main content

% 16k.es

Traps and signals in bash

Restoring an old post from an old blog about using traps and signals in bash.

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 or until
  • 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.

Leer este post en español