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 actually managed to figure out a way to do this without PID files or co-routines and in a way that should work in all POSIX-compatible shells (I've tried bash and dash). At least on systems that support /dev/fd/, but that should be pretty much all of them.
It is a bit convoluted, though, so I'm not sure if it is to your liking.
( # A
( # B
( /tmp/foo 2>&1 1>&3 & echo $! >&4 ) | # C
( tee /dev/fd/2 | ( grep -q bar && echo fin >&4 ) ) # D and E
) 4>&1 | ( # F
read CHILD
read STATUS
if [ "$STATUS" = fin ]; then
kill $CHILD
fi
)
) 3>&1
To explain the numerous subshells used herein:
The body of A runs with the normal stdout duplicated to fd 3. It runs the subshells B and F with the stdout of B piped to the stdin of F.
The body of B runs with the pipe from A duplicated on fd 4.
C runs your actual foo command, with its stderr connected to a pipe from C to D and its stdout duplicated from fd 3; that is, restored to the global stdout. It then writes the PID of foo to fd 4; that is, to the pipe that subshell F has on its stdin.
D runs a tee command receiving, from the pipe, whatever foo prints on its stderr. It copies that output to both /dev/fd/2 (in order to have it displayed on the global stderr) and to a pipe connected to subshell E.
E greps for bar and then, when found, writes fin on fd 4, that is, to the pipe that F has on its stdin. Note the &&, making sure that no fin is written if grep encounters EOF without having found bar.
F, then, reads the PID from C and the fin terminator from E. If the fin terminator was properly output, it kills foo.
EDIT: Fixed the missing tee to copy foo's stderr to the real stderr.