I need to start a process, lets say foo
. I would like to see the stdout/stderr as normal, but grep
the stderr for string bar
. Once
I initially wrote a way to do this that involved stream swizzling, but it wasn't very good. Some of the comments relate to that version. Check the history if you're curious.
Here's a way to do this:
(PIDFILE=$(mktemp /tmp/foo.XXXXXX) && trap "rm $PIDFILE" 0 \
&& { foo \
2> >(tee >(grep -q bar && kill $(cat $PIDFILE)) >&2) \
& PID=$! && echo $PID >$PIDFILE ; wait $PID || true; })
Good old-fashioned nightmare fuel. What's happening here?
mktemp
, and call it PIDFILE
PIDFILE
, again for hygeinefoo
; this is done in a compound statement so that &
binds to foo
and not to the whole preceding pipelinefoo
's standard error into a process substitution which waits for bar
to appear and then kills foo
(of which more later)foo
's PID into a variable, write it to the file named by PIDFILE
, then wait for it, so that the whole command waits for foo
to exit before itself exiting; the || true
discards the error exit status of foo
when that happens.The code inside the process substitution works as follows:
tee
the input (foo
's standard error), redirecting tee
's standard output to standard error, so that foo
's standard error does indeed appear on standard errorgrep -q
on the input, which looks for the specified pattern, and exits as soon as it finds it (or when it reaches the end of the stream), without printing anything, after which (if it found the string and exited successfully) the shell goes on to ...kill
the process whose PID is captured in the file named by PIDFILE
, namely foo