ObjectiveThe objective of this tutorial is to describe how to use the bash shell
trapbuiltin to make our scripts able to perform certain actions when they receive a signal or in other specific situations.
- No special requirements
- # - requires given linux commands to be executed with root privileges either directly as a root user or by use of
- $ - requires given linux commands to be executed as a regular non-privileged user
IntroductionWhen writing scripts that are meant to run for a considerable time, it's very important to increase their robustness by making them able to react to system signals, executing specific actions when some of them are received. We can accomplish this task by using the bash
What are traps?A trap is a bash mechanism which allows to customize a script behavior when it receives a signal. This is very useful, for example, to make sure that the system is always in a consistent state. Imagine you have written a script which during its runtime needs to create some directories: if, for example a SIGINT signal is sent to it, the script will be interrupted, leaving behind the directories it created. Using traps we can handle situations like this.
Trap syntaxTrap syntax is very simple and easy to understand: first we must call the trap builtin, followed by the action(s) to be executed, then we must specify the signal(s) we want to react to:
trap [-lp] [[arg] sigspec]Let's see what the possible
trapoptions are for.
When used with the
-lflag, the trap command will just display a list of signals associated with their numbers. It's the same output you can obtain running the
$ 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 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) SIGRTMAXIt's really important to specify that it's possible to react only to signals which allows the script to respond: the
SIGSTOPsignals cannot be caught, blocked or ignored.
Apart from signals, traps can also react to some
pseudo-signalsuch as EXIT, ERR or DEBUG, but we will see them in detail later. For now just remember that a signal can be specified either by its number or by its name, even without the
-poption now. This option has sense only when a command is not provided (otherwise it will produce an error). When trap is used with it, a list of the previously set traps will be displayed. If the signal name or number is specified, only the trap set for that specific signal will be displayed, otherwise no distinctions will be made, and all the traps will be displayed:
$ trap 'echo "SIGINT caught!"' SIGINTWe set a trap to catch the SIGINT signal: it will just display the "SIGINT caught" message onscreen when given signal will be received by the shell. If we now use trap with the -p option, it will display the trap we just defined:
$ trap -p trap -- 'echo "SIGINT caught!"' SIGINTBy the way, the trap is now "active", so if we send a SIGINT signal, either using the kill command, or with the CTRL-c shortcut, the associated command in the trap will be executed (^C is just printed because of the key combination):
Trap in actionWe now will write a simple script to show trap in action, here it is:
The above script just tries to download the latest linux kernel tarball into the directory from what it is launched using
#!/usr/bin/env bash # # A simple script to demonstrate how trap works # set -e set -u set -o pipefail trap 'echo "signal caught, cleaning..."; rm -i linux_tarball.tar.xz' SIGINT SIGTERM echo "Downloading tarball..." wget -O linux_tarball.tar.xz https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.13.5.tar.xz &> /dev/null
wget. During the task, if the SIGINT or SIGTERM signals are received (notice how you can specify more than one signal on the same line), the partially downloaded file will be deleted.
In this case the command are actually two: the first is the
echowhich prints the message onscreen, and the second is the actual
rmcommand (we provided the -i option to it, so it will ask user confirmation before removing), and they are separated by a semicolon. Instead of specifying commands this way, you can also call functions: this would give you more re-usability. Notice that if you don't provide any command the signal(s) will just be ignored!
This is the output of the script above when it receives a SIGINT signal:
$ ./fetchlinux.sh Downloading tarball... ^Csignal caught, cleaning... rm: remove regular file 'linux_tarball.tar.xz'?A very important thing to remember is that when a script is terminated by a signal, like above, its exist status will be the result of
128 + the signal number. As you can see, the script above, being terminated by a SIGINT, has an exit status of
$ echo $? 130Lastly, you can disable a trap just by calling
trapfollowed by the
-sign, followed by the signal(s) name or number:
trap - SIGINT SIGTERMThe signals will take back the value they had upon the entrance to shell.
Pseudo-signalsAs already mentioned above, trap can be set not only for signals which allows the script to respond but also to what we can call "pseudo-signals". They are not technically signals, but correspond to certain situations that can be specified:
EXITis specified in a trap, the command of the trap will be execute on exit from the shell.
ERRThis will cause the argument of the trap to be executed when a command returns a non-zero exit status, with some exceptions (the same of the shell errexit option): the command must not be part of a
untilloop; it must not be part of an
ifconstruct, nor part of a
||list, and its value must not be inverted by using the
DEBUGThis will cause the argument of the trap to be executed before every simple command,
selectcommands, and before the first command in shell functions
RETURNThe argument of the trap is executed after a function or a script sourced by using