Signals and Scripts
 
John Lees 
This article explains what UNIX signals are and why
you might want 
to catch them in a shell script. I take a look at the
signal handling 
abilities of the Bourne and Korn shells, the C shell,
and perl. Examples 
show how you can use signal handling to make your system
administration 
scripts friendlier. 
Signals 
When an important asynchronous event occurs, the operating
system 
sends a signal to the affected process. Signals are
sometimes called 
interrupts, because they interrupt the normal execution
of a process. 
Some events that can cause signals to be sent are hanging
up your 
connection, typing Control-C, dividing by zero, and
running out of 
memory. 
When you type Control-C, for example, the operating
system sends a 
SIGINT signal to the process in the foreground. If you
are sitting 
at the shell prompt or typing a command line, this process
is your 
login shell; otherwise, it is the process you are running
(in a pipe, 
usually the final process). 
If the process receiving the signal is a shell interpreting
a script, 
you can write the script to catch some signals and do
something appropriate 
in response. For example, when the user types that Control-C,
the 
default action of a shell is to stop executing the script,
which may 
leave an untidy mess of temporary files behind. 
You can write the script to remove temporary files and
perform 
other cleanup actions. You can even choose to ignore
a Control-C, 
though a Control-Z can stop the process. Then the process
can be killed. 
Table 1 shows some signals that are found in most flavors
of UNIX. 
These are the signals that can originate from something
a user does. 
There are many other signals, corresponding to such
asynchronous events 
as illegal instructions, memory errors, and so on. It
generally makes 
no sense for a shell script to catch such signals. 
Signals may be referenced by name or by number. In Bourne
and C shell 
scripts you use the signal number; in perl, you use
the name but without 
the "SIG". See /usr/include/sys/signal.h and
the man 
pages for kill, signal, and termio for detailed 
information on the signals supported by your operating
system. 
You can do one of three things with these signals. Let
the shell take 
its default action (terminate the script). Ignore the
signal. Catch 
the signal and do something useful. 
As an aside, when you fork a child process and then
wait for it to 
terminate, you are using signal handling. The parent
process receives 
a signal when a child process dies. The wait just puts
the parent 
to sleep until this happens. 
Scripts 
There are some basic differences in the signal handling
capabilities 
of the three script languages (the Bourne and Korn shells
have the 
same facilities). The Bourne shell provides a facility
to execute 
an arbitrary sequence of commands on any signal and
also on normal 
exit (signal "0"). The latter is very handy
for doing miscellaneous 
cleanup like removing temporary files. Different sequences
of commands 
can be associated with different signals. 
The C shell provides a transfer of control on signals,
but does not 
implement the useful "normal exit" signal
of the Bourne shell. 
One transfer point is used to handle all signals, and
there is no 
way to resume processing after handling a signal. In
a C shell script 
you can use this transfer to clean up temporary files
before exiting, 
but that is pretty much the extent of what you can do
-- just one 
more reason why the C shell is the last choice for writing
system 
administration scripts. (Some other reasons are the
difficulty of 
redirecting standard error and the lack of shell procedures.) 
The rich and wonderful perl language lets you specify
a routine 
to be executed for each signal. A common signal handler
can easily 
tell which signal has occurred (the name of the signal
is supplied 
to the handler routine as an argument). Version 5 of
perl provides 
an "END" action much like that of awk that
can be 
used to clean up at termination. 
Examples 
The first example is looking through the files in a
user's home directory 
and compiling a list of files for likely removal. The
list is in the 
common "ls -lR" format, which shows the maximum
amount of 
information without generating excessively long path
names that tend 
to wrap around on the monitor. The user is given several
options as 
to how to proceed: quit, generate a file of removal
commands for later 
execution (perhaps after editing, and to serve as a
record of what 
is removed), or interactive removal right now. Figure
1 is an example 
Bourne shell implementation; Figure 2 is the perl version. 
This example makes a very simple match against filename
suffixes. 
You may want to use a longer list of suffixes, plus
perhaps checking 
for files older than, say, two years, of size zero,
larger than some 
maximum size, and so on. The last time my server crashed
and all 
the disks were fsck'd, I noticed that there were over
160,000 
user files. I bet there is some deadwood in there! I'm
going to be 
encouraging my users to use this script, you betcha. 
Figure 3 is an example Bourne shell script that waits
thirty seconds. 
If the user hits Control-C, he or she is informed of
the time remaining 
to wait, but the wait goes on. Figure 4 is an attempt
at doing this 
in the C shell. It illustrates the shortcomings of the
C shell in 
this area. 
Figure 5 shows how to use the BEGIN and END actions
in version 5 of 
perl. Version 5 of perl is now available and I encourage
you to make 
the switch. Scripts written in perl version 4 will work
with perl 
5 with little or no modification. 
Which Script Language? 
The Bourne shell is undoubtedly the first choice for
portable system 
administration scripts. This shell is found on all UNIX
systems 
with little or no variation. If your scripts must run
everywhere, 
use the Bourne shell. 
Although the Korn shell offers many enhancements for
interactive use, 
it is basically identical to the Bourne shell for writing
scripts. 
The Korn shell is not commonly found on BSD systems. 
Bash (The FSF's Bourne again shell) is acceptable and
is freely available, 
but is of course not found on all systems. Because perl
is likely 
to be available on any system that has Bash, I would
not consider 
Bash, except that for true portability you should make
certain that 
your Bourne shell scripts run under Bash. 
The C shell is now quite common, but it is so weak that
I would not 
use it for this purpose. The same comment applies to
variants such 
as tcsh. 
perl is the ideal choice for writing system administration
scripts, 
but it is not yet a standard part of most operating
system distributions. 
I have heard that Sun intends to distribute perl with
Solaris, but 
I don't know when this will begin. I do expect that
eventually perl 
will be found on any system that dares to call itself
UNIX. 
perl programs are scripts in the sense that they remain
in source 
form. A perl script is "compiled" each time
it is run, which 
is why you are informed of syntax errors before any
action takes place. 
With Bourne or C shell scripts, syntax errors are not
found until 
that part of the script is executed. (This is why funny
things happen 
when you edit your .login file. If you start a window
manager 
from your .login file, your login shell may still be
interpreting 
the .login file when you quit the window manager and
logout. 
The shell keeps a pointer to where it should resume
interpreting. 
If you edit the .login file, that pointer may now be
to the 
middle of a line.) 
Summary 
Making your scripts signal-smart is a good idea. You
can leave fewer 
junk files lying around and make life easier for the
users. Learn 
perl. By the time you read this, the new improved perl
5 should be 
widely available. 
Bibliography 
Schwartz, Randal. Learning Perl. Sebastopol, CA: O'Reilly
& 
Associates, 1993. ISBN: 1-56592-042-2.  
Wall, Larry, and Randal Schwartz. Programming perl.
Sebastopol, 
CA: O'Reilly & Associates, 1991. ISBN: 0-937175-64-1.
 
 
 About the Author
 
John Lees has an M.S. in computer science and has worked
during 
the past twenty years about equally as a teacher, technical
writer, 
programmer, and system administrator. His computer experience
began 
in the days of front panels and paper tape, and he doesn't
have enough 
fingers and toes to count the operating systems he has
used. His love/hate 
relationship with UNIX dates to early 1985. Currently
Mr. Lees is 
a systems analyst with the Department of Computer Science,
and manager 
of the Pattern Recognition and Image Processing Laboratory,
at Michigan 
State University. He is a member of ACM, Computer Professionals
for 
Social Responsibility, the League for Programming Freedom,
the Society for
Technical Communication, and the TeX Users Group. 
 
   
  |