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.)
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
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.
Method 2
The script won’t receive the signal until the child process completes. But if you run it in the background and then wait on it, the parent will get the signal:
(while true; do aplay test.wav; done) & wait $!
Method 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.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0