First, you should know that kill 0 send the signal to all process in current process group. (see kill(1) - Linux man page) My guess is that it is killing more than it should. This is why wait is not waiting, it is being terminated for some reason. My best bet to solve this problem is to iterate over running jobs from jobs -pr killing one by one: this way I assure the signal is sent only to child processes in that moment and nothing more.
To get this to work I did several tests, but I was stuck on sending SIGINT to child processes. They simple don't react to it! Searching the web I've found this answer on Stack Overflow:
https://stackoverflow.com/questions/2524937/how-to-send-a-signal-sigint-from-script-to-script-bash
So the real problem is that you cannot send SIGINT from one script to other, because in non-interactive shells the signal is ignored. I was not able to workaround this issue (using bash -i to call the child script does not to work).
I know you probably want to send SIGINT and wait for the child processes to shutdown gracefully, but if you do not mind to use SIGTERM (or any other signal but SIGINT) this is the best script I wrote:
#!/bin/bash trap 'killall' INT killall() { echo '**** Shutting down... ****' jobs -pr for i in $(jobs -pr); do kill -TERM $i done wait echo DONE } ./long.sh & ./long.sh & ./long.sh & cat # wait forever
To test, I've created this long.sh script:
#!/bin/bash trap 'killall' TERM echo "Started... $$" >> file.txt killall() { # shutting down gracefully echo "Finished... $$" >> file.txt exit # don't forget this! } # infinite loop while true; do echo loop >/dev/null ; done
In this last script I did not use sleep function because it creates a new process that was staying on memory even after the script finishes. In your script, you can call new processes but you should assure to broadcast the SIGTERM (or any other signal you've used) to all of them.