Yesterday I got puzzled when a supposedly simple test program did not act as expected.
The shell script trap.sh below sets a trap to catch SIGINT (or signal 2) and exit upon receiving it. It works as expected when run standalone but when invoked as a background process in another test script trapWrapper.sh it failed.
| trap.sh | trapWrapper.sh |
#!/bin/sh # Catch signal 2 trap "echo trapped 2;I=1" 2 # Wait until var 'I' is set to something while : ; do sleep 1; [ -n "$I" ] && break done echo DONE |
#!/bin/sh # Run trap.sh in the background ./trap.sh 2>&1 & pid=$! # Sleep 5 seconds sleep 5 # ... and then kill the background process kill -2 $pid # Wait wait $pid # Exit with exit code of background process exit $? |
| Running trapWrapper.sh will wait forever and never end. When killing it with Ctrl-C it will go away but the trap.sh process will be left behind and needs to be killed manually. | |
So what's the difference when run in background?
The sh man page has the answer:
man sh
...
Signals
The INTERRUPT and QUIT signals for an invoked command are
ignored if the command is followed by &. Otherwise, signals
have the values inherited by the shell from its parent, with
the exception of signal 11 (but see also the trap command
below).
i.e. SIGINT in a background process is ignored (as well as SIGQUIT).SIGTERM is not mentioned here so the next idea is to enhance trap.sh and adding a signal handler for it and changing trapWrapper.sh so that it sends SIGTERM to the background process.
| trap.sh | trapWrapper.sh |
#!/bin/sh # Catch signal 2 (INT) and 15 (TERM) trap "echo trapped 2;I=1" 2 trap "echo trapped 15;I=1" 15 # Wait until var 'I' is set to something while : ; do sleep 1; [ -n "$I" ] && break done echo DONE |
#!/bin/sh # Run trap.sh in the background ./trap.sh 2>&1 & pid=$! # Sleep 5 seconds sleep 5 # ... and then kill the background process kill -15 $pid # Wait wait $pid # Exit with exit code of background process exit $? |
after 5 seconds this will result in what we wanted:trapped 15 DONE |
Something to remember: the supposedly stronger kill with SIGINT (and SIGQUIT would be the same) does not work due to the ignored signal whereas SIGTERM works fine.
So if you write a script which should act upon SIGINT or SIGQUIT let is also act upon SIGTERM, just to be safe.
Note: this might be different in other shells. When you test this interactively you'll see the difference:
| sh | csh |
$ trap.sh&
8379
$ ptree 8379
8360 sh
8379 /bin/sh ./trap.sh
8385 sleep 1
$ kill -2 8379
$ ptree 8379
8360 sh
8379 /bin/sh ./trap.sh
8785 sleep 1
$ kill 8379
$ trapped 15
DONE
|
% trap.sh&
[1] 8116
% ptree 8116
33465 -csh
8116 /bin/sh trap.sh
8207 sleep 1
% kill -2 8116
% trapped 2
DONE
|
| the background process ignores the signal | the background process in csh accepts SIGINT and exits |
No comments:
Post a Comment