Search code examples
bashprocessscriptingcygwinterminate

How to terminate script's process tree in Cygwin bash from bash script


I have a Cygwin bash script that I need to watch and terminate under certain conditions - specifically, after a certain file has been created. I'm having difficulty figuring out how exactly to terminate the script with the same level of completeness that Ctrl+C does, however.

Here's a simple script (called test1) that does little more than wait around to be terminated.

#!/bin/bash

test -f kill_me && rm kill_me

touch kill_me
tail -f kill_me

If this script is run in the foreground, Ctrl+C will terminate both the tail and the script itself. If the script is run in the background, a kill %1 (assuming it is job 1) will also terminate both tail and the script.

However, when I try to do the same thing from a script, I'm finding that only the bash process running the script is terminated, while tail hangs around disconnected from its parent. Here's one way I tried (test2):

#!/bin/bash

test -f kill_me && rm kill_me

(
    touch kill_me
    tail -f kill_me
) &

while true; do
    sleep 1
    test -f kill_me && {
        kill %1
        exit
    }
done

If this is run, the bash subshell running in the background is terminated OK, but tail still hangs around.

If I use an explicitly separate script, like this, it still doesn't work (test3):

#!/bin/bash

test -f kill_me && rm kill_me

# assuming test1 above is included in the same directory
./test1 &

while true; do
    sleep 1
    test -f kill_me && {
        kill %1
        exit
    }
done

tail is still hanging around after this script is run.

In my actual case, the process creating files is not particularly instrumentable, so I can't get it to terminate of its own accord; by finding out when it has created a particular file, however, I can at that point know that it's OK to terminate it. Unfortunately, I can't use a simple killall or equivalent, as there may be multiple instances running, and I only want to kill the specific instance.


Solution

  • /bin/kill (the program, not the bash builtin) interprets a negative PID as “kill the process group” which will get all the children too.

    Changing

    kill %1
    

    to

    /bin/kill -- -$$
    

    works for me.