6

The following Bash loop stops if I interrupt it with ^C:

(while true; do sleep 5; done)

The following, however, does not:

(while true; do aplay test.wav; done)

The difference, from what I can tell, is that aplay catches and handles SIGINT, whereas sleep does not.

Why is this? Does POSIX specify this behavior somewhere (I notice that Dash does the same thing), and if so, why? I cannot quite understand why this behavior is desirable. In fact, how can Bash even tell the difference? I must admit I'm not aware of any mechanism (other than ptrace or /proc/ hacking, at least) by which one process can tell how another one handles signals.

Also, is there a way to counteract it? I notice that not even a trap 'exit 0' SIGINT before the loop helps.

(EDIT: Running the loops in a subshell is important, because otherwise the parent shell does not receive the SIGINT.)

6

The problem is well explained here, as WCE wait and cooperative exit and I encourage you to read it. Basically, your signal is received by all foreground processes, ie the shell and the program, sleep or aplay. The program exits with return code 130 if it does not handle the signal. The shell does a wait for the child to end, and sees this, and the fact that it got the signal too, so exits.

When the program captures the signal, it often just exits with code 1 (as with aplay). When the shell waits for the child, it sees that it did not end due to the signal and so has to assume the signal was a normal aspect of the program's working, so the shell carries on as normal.

For your example, the best way to handle aplay is to check its return code for non-zero and stop:

(while aplay test.wav; do :; done)

The above-mentioned article goes on to explain that a well-behaved program that wants to trap sigint to do some cleanup, should then disable its handler, and re-kill itself in order to get the correct exit flags set.

  • That certainly seems correct, though I find the behavior somewhat puzzling (I don't quite understand why a proper subshell wouldn't just want to quit from the signal). I'll have to investigate further. – Dolda2000 Jun 4 '16 at 0:11
  • @Dolda2000: think of what you'd want this script to do: "#!/bin/bash\n emacs foo.c \n rsync -av foo.c user@backup_server:/src/". If all scripts died immediately when their children got sigints, this kind of script would not work (your code would never sync up remotely). The script needs to know if its child died because of the sigint (in which case it must stop further execution) or just exited - in which case it must continue. Arguably, 'aplay' has a bug - it doesn't communicate the SIGINT as a cause of death - and just exits. – ttsiodras Jun 11 '16 at 19:20
  • @ttsiodras: "your code would never sync up remotely" -- How so? Are you implying that Emacs only exits because of SIGINTs? That's unlike the Emacs I know, at least. – Dolda2000 Jun 12 '16 at 1:51
  • @Dolda2000: I mean that the rsync that is supposed to execute after emacs finishes, would not be executed - because in your scenario "the subshell would quit after the signal". – ttsiodras Jun 13 '16 at 5:51
  • 1
    The program does not actually exit with code 130. unix.stackexchange.com/questions/386836 – JdeBP Feb 15 at 18:49
-3

you could try this, i had this issue a few times and idk why this worked by it did.

$(sigint)

this is to dislay the result like a var is bash, but to do so it has to run it and some times works.

  • I'm not sure what you're referring to here. There's no sigint command on my system, at least. – Dolda2000 Jun 2 '16 at 0:54
  • I got it mixed up with a different command, from angryip. signint -f it returns the ips without the periods. Sorry about that. – Dillon Burnett Jun 2 '16 at 1:15

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.