8

I have a small script to demonstrate what I want to do

#!/bin/bash
> z
tail -f z | grep 'd' &
echo $!

The $! gives the PID of the grep process. I want to be able to kill the tail process at the same time as killing the grep process. Doing kill "pid of grep"does not kill the tail process. Nor does killall grep. I could use killall tail but I think this would be dangerous.

3 Answers 3

7

Enclose your command with parentheses:

( tail -f z | grep 'd' ) &
kill -- -$!

This will kill the whole sub-process.

Here, by specifying a negative PID to kill, we kill the whole process group. See man 1 kill:

Negative PID values may be used to choose whole process groups; see the PGID column in ps command output.

Or man 2 kill:

If pid is less than -1, then sig is sent to every process in the process group whose ID is -pid.

However, kill -PID will only work if job control is enabled in bash (the default for interactive shells). Else, your subprocess won't have a dedicated process group and the kill command will fail with kill: (-PID) - No such process

To work around that, either activate job control in bash (set -m), or use pkill -P $!

7
  • This doesn't seem to work. I executed this script and echoed the $!. Then I killed the process and appended "r", then "d". But the grep was still running and it echoed the "d" to stdout. Furthermore, killing that pid still shows the tail and grep in ps ux Jan 12, 2017 at 15:21
  • @user1005909 Sorry, I answered too quickly. Answer updated
    – xhienne
    Jan 12, 2017 at 15:24
  • 1
    Looks like there's an extra - on -$!. Anyway, this works only if the kill statement is in the bash script. Echoing the pid, then using kill -- pid from another shell doesn't work. Jan 12, 2017 at 15:27
  • 1
    I see. I was getting -bash: kill: (-pid) - No such process. But this lead me to use pkill -P pid which works as I want. Jan 12, 2017 at 15:36
  • 1
    @user1005909 That's weird. I have never had any issue with the command kill -- -PID. I've just tried it again with Debian, Ubuntu and SLES, and it works fine.
    – xhienne
    Jan 12, 2017 at 15:42
0

I tried this and it didn't work for me

( tail -f z | grep 'd' ) &
kill -- -$!

I just used a named pipe instead of an unnamed pipe

rm -f tailfifo; mkfifo tailfifo
tail -n 0 -f mylog.log >tailfifo &
TAIL_PID=$!
grep --line-buffered someword <tailfifo >outputfile &
GREP_PID=$!
# .. ..
kill $TAIL_PID
kill $GREP_PID
1
  • 1
    You tried what?  The other answer?  If you refer to another answer, please say so clearly. Jul 25, 2018 at 23:44
0

xhienne's answer does not work in non-interactive sh (dash) . use () will not create a process group, so sh -c '(sleep 6m | sleep 8m) & kill -- -$!' will not work.

However, you can still use ps --no-headers --format pgid:1 $! to find the pgid of the whole pipe and kill it.

sleep 6m | sleep 8m &
pgid=`ps -ho $!`
kill -- -$pgid

Moreover, using () in pipe will cause shell not create process group.

pgid() {
    ps --no-headers --format pgid:1 $1
}

while true
do nc $ip $port
done | str2str -out tcpsvr://:5566 &
echo `pgid $!` == $$
wait

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .