Cette page n'est pas disponible en français. Veuillez-nous en excuser.

ABIS Infor - 2011-10

UNIX shell scripting tips & tricks

Koen De Backer (ABIS) - 30 August 2011

Abstract

A robust script should always take care of its environment, one of the aspects of this caretaking is the ability to clean up any temporary logs or processes left lying around from an unexpected termination. Another element to consider is when an interrupt from a user is received, what is the sensible action to be taken?

Furthermore a technique to detect error situations encountered by the script and the use of the system logging infrastructure to leave traces for later troubleshooting can make a script more useful for routine jobs.

Introducing signals

Scripts can be killed or terminated using the signal mechanism (not all signals sent are terminal). A signal that is sent to a running process interrupts that process to force some sort of event, typically some action. Upon receiving a signal, the script can do one of three actions:

  1. Ignore it and do nothing. (true for most scripts)
  2. Catch the signal using "trap" and take the appropriate action.
  3. Take the default action.

All the above is true except for the following signals: SIGKILL (9) - SIGSTOP (17) - SIGCONT (18). These cannot be caught and always uses the default action.

A tool to work with signals from the shell

To catch a signal to a process, use the built-in "trap" command. If certain signals are ignored, the default action always takes place. For example, if you only trap for SIGINT but do nothing about SIGQUIT, then if your process gets a SIGQUIT, the default action takes place (most likely a termination of the script, leaving a lot of loose ends that need to be taken care off). The following peace of code shows how the trap command can be used in a script:

#!/bin/bash
# scriptA
# trap command embedded in script
trap 'echo Ctrl-C/Ctrl-\ detected, now exiting...; exit' SIGINT SIGQUIT
...

The most basic task in care taking at termination time is to clean up temporary files. Typically, these are created with the PID (the process pid) using the built-in variable $$ that gets appended to the user created files in /tmp. The following piece of code traps for SIGHUP SIGINT SIGQUIT SIGTERM then removes the files:

trap 'rm /tmp/sc_log*.$$; exit' SIGHUP SIGINT SIGQUIT SIGTERM

The finishing touch

Other stuff the "trap" command can do for your script includes trapping a non-zero exit status from a command, using the ERR variable. The ERR word goes with the signal list within the trap command. The following incomplete piece of code shows how to write this construct (a non-existent file is copied, which causes an error and triggers "trap"):

#!/bin/bash
# scriptB
trap 'echo An error again,...' ERR
cp /home/auser/phantomfile /tmp
...

There are also two variables to be mentioned when dealing with trap events to give more information on the script termination, LINENO and BASH_COMMAND. The BASH_COMAMND is exclusive to bash. These report the line number that the script is currently executing, and the current command that is running respectively. The next code lines show how these can be used, the "logger" command comes with syslogd.

#!/bin/bash
# scriptC
trap 'fn_exit $LINENO $BASH_COMMAND; exit' SIGHUP SIGINT SIGQUIT

fn_exit()
{
echo "$(basename $0) ran into an error on line: $1 command was: $2"
logger -p notice "script: $(basename $0) was terminated on line: $1, command was $2"
# take care of the loose ends here
...
}

Conclusion

Adding code to a script based on the trap command, implementing the system log functionality will ask some effort when building the script and while creating the logic around the commands mentioned (trap, logger). The advantage will be that the script will behave more efficiently when it runs into signals and probably time will be saved when troubleshooting unexpected situations encountered by the same script.

The "UNIX/Linux/AIX fundamentals course" (see http://www.abis.be/html/en0829.html) explores the shell environment and its commands (what, how, when) in more depth.

Developing scripts and creating efficient functionality based on shell features is discussed in "UNIX/Linux/AIX shell programming" (see http://www.abis.be/html/en0953.html).