Kill the previous command in a pipeline
Asked Answered
S

3

5

I am running a simulation like this

./waf --run scratch/myfile | awk -f filter.awk 

How can I kill the waf command as soon as filter.awk detects that something happened (e.g. after a specific line is read)?

I cannot change waf or myfile. I can only change filter.awk, and the above command (obviously).

Update after comments:

  • waf does not terminated after receiving SIGPIPE (as it should?)
  • It spawns child processes, that need cleaning up.

This is my own answer (and challenge).


After working on @thatotherguy's ans @Chris's answers, I simplified a bit and got this:

tmp=$(mktemp)
{ ./waf --run scratch/myfile & echo $! > "$tmp"; } | { awk -f filter.awk; pkill -P $(<$tmp); kill $(<$tmp); }

Unfortunately I could not get rid of the tmp file, every attempt to pass the PID as a variable failed.

I won't change the accepted answer (since it was the one that worked when it was really needed), but +1 for anyone that can simplify more.

Sherl answered 15/2, 2013 at 16:43 Comment(2)
You can use pkill -P $(<$tmp); kill $(<$tmp) to kill all processes whose PPID is your waf instance, and finally the waf instance itself.Hatchet
@thatotherguy Yes with that change a get no processes left running. Would you like to convert the comment to an answer so that I can accept it? Thank you very much for your help! I would never solve it by my self...Sherl
H
2

What makes this tricky is that waf misbehaves by not exiting when the pipe breaks, and it spawns off a second process that we also have to get rid off:

tmp=$(mktemp)
cat <(./waf --run scratch/myfile & echo $! > "$tmp"; wait) | awk -f filter.awk; 
pkill -P $(<$tmp)
kill $(<$tmp)
  • We use <(process substitution) to run waf in the background and write its pid to a temp file.
  • We use cat as an intermediary to relay data from this process to awk, since cat will exit properly when the pipe is broken, allowing the pipeline to finish.
  • When the pipeline's done, we kill all processes that waf has spawned (by Parent PID)
  • Finally we kill waf itself.
Hatchet answered 15/2, 2013 at 19:12 Comment(0)
P
4

Use awk's exit statement. waf should exit as soon as the pipe connecting it to awk closes.

Privation answered 15/2, 2013 at 16:50 Comment(11)
Tried with exit -1 and exit 0. awk terminates, but waf keeps running.Sherl
Does waf output more lines shortly after awk exits? Otherwise it won't notice awk's not there anymore.Hatchet
@thatotherguy I filtered lines starting with TraceDelay for testing. Without exit, it prints continuously. With exit, printing stops, but prompt does not return (pressing enter does not help). I will update question to clarifySherl
See if waf --run scratch/myfile | false also hangs. If it does, you can use cat <(waf --run scratch/myfile) | awk -f filter.awk. The pipeline should then end, and you can killall waf or similar afterwardsHatchet
@thatotherguy: feel free to edit my answer (or add your own), as I don't fully understand how using cat as an intermediary fixes the problem. Is it an issue of waf not trying to write to the closed pipeline?Privation
Unaccepting because I am left with the myfile proccess running afterwords. Sorry :)Sherl
That was what the killall waf part was for. If myfile becomes a separate process, you'd have to kill that manually too.Hatchet
@Privation Apparently waf just ignores the write error and SIGPIPE, and keeps on processing. cat does not misbehave in this way, so when the pipe is broken, it exits, allowing the pipeline to end.Hatchet
@thatotherguy Thank you for your help. With kill XXXX, where XXXX is the PID of myfile i can kill it manually. But I want to do it automatically so that it can be integrated in a scriptSherl
@Sherl kill works in scripts as well. To get the specific pid, you can use: tmp=$(mktemp); cat <(waf .. & echo $! > "$tmp"; wait) | awk ..; kill -9 $(<$tmp)Hatchet
@thatotherguy I updated my answer with the result of your last comment. I think you are close to finding a solution. The only problem is that the parent process terminated, but not the child process. kill -SIGINT did not help within the script.Sherl
H
2

What makes this tricky is that waf misbehaves by not exiting when the pipe breaks, and it spawns off a second process that we also have to get rid off:

tmp=$(mktemp)
cat <(./waf --run scratch/myfile & echo $! > "$tmp"; wait) | awk -f filter.awk; 
pkill -P $(<$tmp)
kill $(<$tmp)
  • We use <(process substitution) to run waf in the background and write its pid to a temp file.
  • We use cat as an intermediary to relay data from this process to awk, since cat will exit properly when the pipe is broken, allowing the pipeline to finish.
  • When the pipeline's done, we kill all processes that waf has spawned (by Parent PID)
  • Finally we kill waf itself.
Hatchet answered 15/2, 2013 at 19:12 Comment(0)
C
1

When awk exits, waf gets a SIGPIPE the next time it tries to write output, which should cause it to exit unless whoever wrote waf deliberately set it up to ignore SIGPIPEs telling it to exit, in which case you'll have to kill it manually with kill or some such. Something like:

./waf --run scratch/myfile | ( awk -f filter.awk; killall waf )

You might need a -KILL option to killall if waf is ignoring all exit signals.

Cancer answered 15/2, 2013 at 17:15 Comment(2)
With this I got waf: no process found, but no prompt.Sherl
Also would killall affect other waf instances running from the same script?Sherl

© 2022 - 2024 — McMap. All rights reserved.