Trap: A Shell built-in Command

You might have come across a few shell scripts on the Internet, which are used in production, that use a keyword called trap. Naturally, you are wondering what the heck is this trap keyword, how is it useful and how do I use it in my scripts?

All of these questions will be answered in this article :)

What is the trap command?

When you use a command like ls or cd, they are provided by a third-party utility/package. In this case, the ls and cd commands are provided by the GNU Coreutils package.

But, when you create a Shell script, you will be using keywords like if, else, elif, fi and more. These are not some binaries living inside /usr/bin. They are the commands that are part of the POSIX-compliant shell that you are using.

Similar to the if, else and other keywords, there is the trap command built-in. This trap built-in is used to trap (catch) any supported signals and react upon it.

The list of supported signals can be checked by running the trap -l command.

Following is the output from my computer:

$ 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) SIGRTMAX
💡
The number before a signal is not a random number or just an index. Instead of using the signal, you can use the number to its right and the same signal will be triggered or trapped.

Meaning, if you do a kill -9 PID, you are sending the SIGKILL signal to said PID.

Though, it is largely recommended to use these signals because it makes the code easier to read ;)

When the trap built-in is used, it will listen for signals that it supports. If the trap built-in is told to check for a signal, when that signal is received by the script, trap will execute the action you mentioned.

Using the trap command

The best practice is practicals. No one said this; I just came up with it. But still pretty accurate, wouldn't you agree?

But first, let me bore you just a bit with the syntax:

trap ACTIONS SIGNALS

Here, substitute SIGNALS with one or more signals that you want to monitor within the script. Once the trap built-in catches even one of the mentioned signals, any ACTIONS (it is usually a function call) that you provide will be performed.

Listening for supported signals

The easiest way to use trap is for a clean-up of sorts. Let's say that you are running a task in one of the shell scripts.

As the script is running, a user presses the Ctrl + C key combination. How would you clean-up the artifacts now? Simple, use the trap built-in.

Let me demonstrate this with a simple Bash script. Its contents are as the following:

#!/usr/bin/env bash

function sig_int() {
    echo "The sleep was disturbed by Jarasandha"
}

trap sig_int SIGINT

sleep 10m

There is a function called sig_int which, when called will echo the string "The sleep was disturbed by Jarasandha". Under that, there is the use of the trap built-in. It is listening for the SIGINT signal and upon receiving it, it will call the sig_int function.

Let's try this!

$ ./trap-script.sh
^CThe sleep was disturbed by Jarasandha

As soon as I pressed the Ctrl + C key (as evident by ^C), the echo statement inside the body of the sig_int function was called.

Sending other signals

SIGINT is not the only signal that you might want to monitor for. Therefore, you may want to verify whether your script is correctly listening for intended signals. In that case, you can send a signal externally using the kill command.

Following is the syntax for this particular use:

kill -s SIGNAL PID

As it might be obvious now, use the -s option to specify which signal you intend to send. Then, specify the PID of the program to which you want to send the signal.

I will make a slight modification to the way I execute this script... I will run it in the background so I can use the same shell to run the kill command to send the signal.

$ ./trap-script.sh &
[1] 28550

$ kill -s SIGINT 28551

$ The sleep was disturbed by Jarasandha

[1]  + exit 130   ./trap-script.sh

Look closely. The PID of the script that was put in the background is 28550 but I am sending SIGINT to the shell script's PID + 1 PID (28551). The reason is that the active process is the sleep command which is running inside our bash script ;)

Check All Running Process in Linux
Want to know what all processes are running on your systems? Here’s how to get details of the running processes in Linux.

You are actually monitoring the signals for the commands running inside the script. Not the script itself.

Conclusion

The trap is a bash built-in that is used to monitor for signals that are usually received by your shell script.

This article practically demonstrates how to listen for supported signals with the example of the SIGINT signal. We also look at how to send a signal using the kill utility.

Of course, there are many more built-ins you can explore.

Shell Built-in Commands List
In Linux, there are shell built-in commands which you are already using but never paid attention to. Learn more about them in this tutorial.

Let me know if you have questions.