1

I have a master script that controls some satellite scripts. When I send the interrupt signal from the terminal, the parent's traps caught the signal, but the child's didn't, which I can't figure out why. I didn't change the default terminal setting (I didn't run stty anywhere).

Here's my parent and child script and terminal output:

parent:

#!/bin/sh

./child.sh &

for sig in $(kill -l) ; do
    trap "echo parent:$sig" $sig
done

wait

child:

#!/bin/sh

cat < /dev/tty &
PID=$!

for sig in $(kill -l) ; do
    trap "echo child:$sig" $sig
done

wait

terminal interaction:

[prompt]$ ./parent.sh
^Cparent:INT
cat: stdin: Input/output error

[prompt]$

Update

I tested the script on macOS and CentOS, the behavior describe above occurs. When I test it on FreeBSD using the default Bourne-compatible shell, the signal child receives is CHLD.

2

For commands run asynchronously by non-interactive shells (actually when job control is not enabled), SIGINT and SIGQUIT are ignored in POSIX compliant sh implementations. That's a POSIX requirement though some shells ignore it.

Another POSIX requirement is that if a signal was ignored upon start of the shell, you can't unignore it, so you're screwed.

Here, you could use zsh instead of sh which doesn't have either of those annoying "features" (at least in current versions).

In any case signal handling in shells is one of the least reliable and portable aspects. You'll find behaviours vary greatly between shells and often between different versions of a same shell. Be prepared for some serious hair pulling and head scratching if you're going to try to do anything non-trivial.

I'd recommand using a different language where you can have a finer control over what happens.

Also, I wouldn't blindly handle every possible signal, only the ones you're expecting to receive and know how you can handle. Trapping SIGCHLD for instance (which the shell itself is interested in as it's its job to spawn processes and handle their termination) is likely not to do what you want.

5
  • Any SIGCHILD trap will be run after the shell has handled the signal (by reaping the child and recording its exit status). Setting a SIGHCLD trap should be pretty harmless. Saying that "it will not do what you want" could be a little bit more explicit -- if it's something else it's alluding to (for instance the fact that a single SIGCHLD handler can be run for multiple children). – zevzek Oct 16 '20 at 9:26
  • @zevzek, that very much depends on the shells, some use wait*(), some use a handler on SIGCHLD and get the info from siginfo_t. For having tested a few years ago, IIRC, some let you trap SIGCHLD, some don't. – Stéphane Chazelas Oct 16 '20 at 9:51
  • My point was that trapping SIGCHLD would be basically harmless. That may be incorrect -- do you know of any shell when setting a SIGCHLD trap will interfere with shell handling of a child termination? Because that's what your last paragraph suggests. – zevzek Oct 16 '20 at 9:54
  • @zevzek, what I meant was that it was unlikely to do what you want whatever it is. You can try "$shell" -c 'trap uname CHLD; id; exit' and compare between shells. You'll find some run uname in a loop, some don't run it at all, some run it once. – Stéphane Chazelas Oct 16 '20 at 10:00
  • That's a great example -- running another child process from the SIGCHLD trap handler will trigger the trap again, recursively. It's much better than suggesting that setting a SIGCHLD trap will override the shell's own signal handler ;-) – zevzek Oct 16 '20 at 10:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.