Trap Zero is a very useful tool in shell scripting when there is a need to clean up the environment upon exit. However I wanted to be sure which of the default signal handlers for a shell would cause trap zero to be fired. Listing 1 shows the script I used to test the signal handling behaviour.
Listing 1 – trap_zero_test.sh
# No hash-bang in this script. We intend it to be run as an argument to the shell to # permit testing against multiple shells. # # trap_zero_test.sh # Script to test which signals will cause trap zero to be executed for the shell # # For each signal under test: # - launch a child process which has the task writing a string to a file upon execution # of trap zero. The child process shall send itself the signal and then sleeps until # timeout after approximately 3 seconds unless the signal handler causes an exit. # - Observe whether trap zero is triggered by examining the content of the written file. # Create the temporary file for child processes to write to. Clean up the file on exit. TEMP_FILE=$(mktemp) trap "rm $TEMP_FILE" 0 # Print a header printf "SignaltNametTrap 0 Executedn" for signal in $(seq 1 15); do printf "$signalt$(kill -l $signal)t" # Clear the temporary file ready for writing to by the child process. : > $TEMP_FILE # Launch the child process using a script customised to the signal under test. $SHELL -s <<-EOSCRIPT # Configured the trap trap "echo Trap0 > $TEMP_FILE" 0 # Send the test signal. kill -$signal $$ # Timeout the process in 3 seconds if not alredy killed. sleep 3 & wait # Reset the trap if it hasn't already been fired. trap 0 EOSCRIPT # Observe whether trap 0 was fired in the child process. result="No" [ "$(cat $TEMP_FILE)" = "Trap0" ] && result="Yes" echo $result done
This script will launch a child process with the task of registering trap zero and sending itself the signal under test. If trap zero is fired it will write the string Trap0 to a temporary file. The parent process will then read the contents of this file to determine whether the trap zero will fire for the particular signal under test.
The script can be executed against different shells using a command lines like (note the redirect of standard error):
- sh trap_zero_test.sh 2>/dev/null
- bash trap_zero_test.sh 2>/dev/null
Testing on Ubuntu 12.04 with sh, bash, dash and ksh the output in Listing 2 was received in each case. Testing on Cygwin with sh and bash the output in Listing 3 was received in both cases.
Listing 2 – Output of test_trap_zero.sh on Ubuntu 12.04 for sh, bash, dash and ksh
Signal Name Trap 0 Executed 1 HUP Yes 2 INT Yes 3 QUIT No 4 ILL Yes 5 TRAP Yes 6 ABRT Yes 7 BUS Yes 8 FPE Yes 9 KILL No 10 USR1 Yes 11 SEGV Yes 12 USR2 Yes 13 PIPE Yes 14 ALRM Yes 15 TERM Yes
Listing 3 – Output of test_trap_zero.sh on Cygwin for sh and bash
Signal Name Trap 0 Executed 1 HUP Yes 2 INT Yes 3 QUIT No 4 ILL Yes 5 TRAP Yes 6 ABRT No 7 EMT Yes 8 FPE Yes 9 KILL No 10 BUS Yes 11 SEGV Yes 12 SYS Yes 13 PIPE Yes 14 ALRM Yes 15 TERM Yes
From Listings 2 and 3 it seems we can rely on Trap 0 being executed in all major signals except QUIT, ABRT and KILL.
The KILL signal will terminate a process immediately with no opportunity for clean-up so it is expected behaviour that trap zero is not triggered in this case.
The ABRT signal is raised by a program when it experiences an abnormal situation that it cannot recover from. Immediately terminating without triggering trap zero in this case seems appropriate as we cannot be sure that the execution of trap will not also be compromised. It is interesting that Linux and Cygwin treat the handling of ABRT differently. If anyone has any info on why this difference occurs please let me know.
The QUIT signal is a keyboard signal and is usually generated by pressing Ctrl- at the terminal. This signal will normally cause a process to immediately terminate and dump core. Once again it is probably appropriate not to execute trap zero in this since any commands in that trap could alter the memory state and affect the resulting core dump.