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.

Community
  • 1
  • 1
user000001
  • 32,226
  • 12
  • 81
  • 108
  • You can use `pkill -P $(<$tmp); kill $(<$tmp)` to kill all processes whose PPID is your waf instance, and finally the waf instance itself. – that other guy Feb 15 '13 at 18:55
  • @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... – user000001 Feb 15 '13 at 19:05

3 Answers3

4

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

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Tried with `exit -1` and `exit 0`. `awk` terminates, but `waf` keeps running. – user000001 Feb 15 '13 at 16:56
  • 1
    Does `waf` output more lines shortly after awk exits? Otherwise it won't notice awk's not there anymore. – that other guy Feb 15 '13 at 16:59
  • @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 clarify – user000001 Feb 15 '13 at 17:01
  • 1
    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 afterwards – that other guy Feb 15 '13 at 17:07
  • @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? – chepner Feb 15 '13 at 17:28
  • 1
    Unaccepting because I am left with the `myfile` proccess running afterwords. Sorry :) – user000001 Feb 15 '13 at 17:29
  • That was what the `killall waf` part was for. If `myfile` becomes a separate process, you'd have to kill that manually too. – that other guy Feb 15 '13 at 18:09
  • 1
    @chepner 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. – that other guy Feb 15 '13 at 18:14
  • @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 script – user000001 Feb 15 '13 at 18:15
  • @user000001 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)` – that other guy Feb 15 '13 at 18:21
  • @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. – user000001 Feb 15 '13 at 18:42
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.
that other guy
  • 116,971
  • 11
  • 170
  • 194
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.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226