4

Is there any way to run two commands in parallel in Bash such that, if the shell is killed forcefully, then both of the commands are guaranteed to be also killed?

A simple & won't work because if I do

sh -c 'sleep 6 & sleep 3'

then the first sleep will continue to linger even if sh is killed.

user541686
  • 3,083
  • Can you expand on the word "guaranteed"? Do you want this to happen if someone uses kill -9?

    Can the thing doing the killing be changed, in particular to specify a process group rather than a process?

    – icarus Dec 24 '18 at 15:37
  • @icarus: no, the killing is outside my control. No matter how it's killed, both children need to die. – user541686 Dec 24 '18 at 15:40

2 Answers2

1

use trap 'kill 0' EXIT in the main script. that will kill all children upon script exit. regardless how script exit. except kill -9. more on that later.

to demonstrate i created two files

$ cat killchildren 
#! /bin/sh
trap 'echo killing all children; kill 0' EXIT # this is the important line
./sleepx 4 &
./sleepx 2
echo killchildren done

$ cat sleepx 
#! /bin/sh
trap 'echo sleep$1 killed' EXIT # just print that it was killed
sleep $1
trap - EXIT # clear trap so not print that it was killed
echo sleep$1 done

here it is just run and left alone

$ ./killchildren 
sleep2 done
killchildren done
killing all children
sleep4 killed

here it is killed with normal kill (note i raised the sleep times because i am not fast typer)

$ ./killchildren &
[1] 13248

$ kill 13248
killing all children
sleep8 killed
sleep10 killed
[1]+  Terminated              ./killchildren

here it is killed with kill -9

$ ./killchildren &
[1] 13259

$ kill -9 13259
[1]+  Killed                  ./killchildren

$ # ... time passes ...
sleep8 done
sleep10 done

note that there was no exit message from the main script and the children were not killed.

not tested much. works at least with sh on arch linux as of 2018.


it will not work if main script is kill -9. because the semantic of kill -9 is to terminate immediately without letting process do anything. not even trap handling.

that said whatever kills your process should not do it with kill -9. i would consider that a bug. here is more information on why not kill -9:

Lesmana
  • 27,439
1

The solution from @lesmana, using trap is the conventional approach but as noted fails to handle the kill -9 case.

There is an alternative, set up a process to monitor the main process and issue a kill when the main process exits. An easy way to do the monitoring is to set up a pipe to the monitor, when the main process exits then the kernel will close the pipe, which the monitor can detect.

Here is an example

#!/bin/bash
# show monitoring main process - Icarus Sparry
# linkedin@icarus.freeuk.com

# Handler function. Reads from stdin. If it doesn't read the word clean
# then send SIGINT to the process group.
handler(){
    read
    [ "$REPLY" = "clean" ] || kill -INT -$$
}

# invoke handler as a coprocess. 
coproc handler

# some work in the background, wait for 20 seconds and print a message
{ sleep 20 ; echo bye 20 ; } &

# some work in the foreground
sleep 5 ; echo bye 5

# At this point tell the coprocess it was a clean end, so don't
# terminate the background
echo clean >&"${COPROC[1]}"

Running this will, after 5 seconds, output bye 5 and 15 seconds later output bye 20. killing the process will cause the coprocess to kill off the background sleep.

If it is desired that the background processing is terminated when the foreground work is completed then omit the final echo clean >&"${COPROC[1]}", and optionally remove the [ "$REPLY" = "clean" ] || from the handler, leaving the read and kill.

icarus
  • 17,920