Work the Shell - Dealing with Signals

 in
By handling signals in your bash scripts, you can provide features that are otherwise difficult, such as telling your script to reread its configuration file after it's already been started.

This month, I thought it would be interesting to take a bit of a detour from my usual multi-month programming projects and instead focus on a specific topic that is of great importance to people writing longer scripts: signal management.

Signals are numeric messages sent to running applications from the operating system, other applications or the user, and they generally invoke a specific response like “shut down gracefully”, “stop running so I can put you in the background” or “die!”

Most likely, you've used the kill command to send signals to different programs, but if you've ever pressed Ctrl-C or Ctrl-Z to stop a running app, you've also sent signals to a running application.

A signal is managed in a cascading manner. It's sent to the application or script, then if the application doesn't have a specific handler (signal management or response function), it's pushed back to the shell or operating system. Some signals can't be managed within individual apps, like SIGKILL, which is caught by the operating system and immediately kills the running application (including the shell: SIGKILL your login shell and you just logged out).

To start this journey, let's find out what signals your version of Linux can handle. Do this by typing kill -l (that's a lowercase L, not the digit 1):

$ kill -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  22) SIGTTOU    23) SIGIO    24) SIGXCPU
25) SIGXFSZ  26) SIGVTALRM  27) SIGPROF  28) SIGWINCH
29) SIGINFO  30) SIGUSR1    31) SIGUSR2

Most of these are uninteresting. The cool ones are SIGHUP, which is sent on a “hangup” or the user logging out; SIGINT, which is a simple interrupt (Ctrl-C, usually); SIGKILL, the “terminate with extreme prejudice” of signals; SIGTSTP, which is Ctrl-Z; SIGCONT, which is what the application gets from the shell commands fg and bg subsequent to a SIGTSTP; SIGWINCH, which is for window system events like a window resize; and SIGUSR1 and SIGUSR2, which are intended for interprocess communication.

Let's write some code to see what happens, shall we? Signals are caught with the “trap” built in, and the general format of these signal mapping commands is exemplified with:

trap 'echo "Ctrl-C Ignored" ' INT

How do we play with that as a shell script? Here's an easy way:

#!/bin/bash

trap 'echo " - Ctrl-C ignored" ' INT
while /usr/bin/true ; do
  sleep 30
done

exit 0

Did you catch the infinite loop there? It's barely using any resources because most of its time is spent in “sleep”, but if you don't do something to end it, this script will run forever or until the Mayans are proven right two years from now—one of the two.

Let's look at a more flexible way to manage signals by creating shell script functions:

sigquit()
{
   echo "signal QUIT received"
}

sigint()
{
   echo "signal INT received, script ending"
   exit 0
}

trap 'sigquit' QUIT
trap 'sigint'  INT
trap ':'       HUP      # ignore the specified signals

echo "test script started. My PID is $$"
while /usr/bin/true ; do
  sleep 30
done

Run this then from another terminal window and shoot some signals at it.

Now, let's get that script started and watch what happens when we send a few different signals:

$ ./test.sh
test script started. My PID is 25309
signal QUIT received
signal INT received, script ending
$

Perfect! To send the signals, execute the following commands from a different terminal window:

$ kill -HUP  25309
$ kill -QUIT 25309
$ kill -INT  25309

Armed with this useful script, let's have a look at how to handle a more complex signal like Ctrl-Z within a shell script.

Stop! Don't Stop!

I'm going to create a scenario here rather than just going through the intellectual exercise. In a complex script, you realize that you have certain passages where you need to ignore the TSTP signal (SIGTSTP or Ctrl-Z or signal number 18) and other spots where it's fine to stop and restart. Can it be done?

To start working out a solution, I'll create a function that not only handles the specified signal, but also disables itself after a single invocation:

sigtstp()
{
  echo "SIGTSTP received" > /dev/tty
  trap - TSTP
  echo "SIGTSTP standard handling restored"
}

Invoke trap - signal somewhere else in the script, and you've reset that signal handler, so if I have the line:

trap 'sigtstp' TSTP

______________________

Dave Taylor has been hacking shell scripts for over thirty years. Really. He's the author of the popular "Wicked Cool Shell Scripts" and can be found on Twitter as @DaveTaylor and more generally at www.DaveTaylorOnline.com.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Passing argument

Anonymous's picture

Hi all,
I wanted to know if it's possible to pass arguments to the configuration file when we invoke the command kill.

For example we want to change directory to a shell 1 from another shell 2, so:
# test.sh
sigusr1()
{
cd $1
}
trap "sigusr1" USR1

type command in shell 1:
$ ./test.sh

then, i would like type something like this in shell 2:
$ kill -USR1 10482 /home

In this way i can specify the new current directory directly from shell 2.

How can I do it? Is there another workaround??
Thanks

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState