petersohn
petersohn

Reputation: 11730

Kill process on the left side of a pipe

I have the following in bash:

foo | bar

I want foo to die when the script is terminated (using a TERM signal). Unfortunately, none of them dies. I tried this:

exec foo | bar

This achieved absolutely nothing. Then I tried this:

function run() {
    "$@" &
    pid=$!
    trap "kill $pid" EXIT
    wait
}

run foo | bar

Again, nothing. Now I have one more process, and none of them dies when I terminate the parent.

Upvotes: 7

Views: 1287

Answers (2)

Camusensei
Camusensei

Reputation: 1563

By killing the whole process group instead of just bash (the parent), you can send the kill signal to all children as well. Syntax examples are:

kill -SIGTERM -$!
kill -- -$!

Example:

bash -c 'sleep 50 | sleep 40' & sleep 1; kill -SIGTERM -$!; wait; ps -ef | grep -c sleep
[1] 14683
[1]+  Terminated              bash -c 'sleep 50 | sleep 40'
1

Note that wait here waits for bash to be effectively killed which takes some milliseconds. Also note that the final result (1) is the 'grep sleep' itself. A result of 3 would show that this did not work as two additional sleep processes would still be running.

The kill manual mentions:

-n
where n is larger than 1. All processes in process group n are signaled.
When an argument of the form '-n' is given, and it is meant to denote a
process group, either the signal must be specified first, or the argument
must be preceded by a '--' option, otherwise it will be taken as the signal
to send.

Upvotes: 1

chepner
chepner

Reputation: 531135

I would use a named pipe, which makes it easier to get the process ID of foo specifically.

trap 'kill $foo_pid; rm -f foo_out' EXIT

mkfifo foo_out
foo > foo_out & foo_pid=$!
bar < foo_out

Upvotes: 1

Related Questions