How can I kill a process when a specific string is seen on standard error?

后端 未结 5 1693
执念已碎
执念已碎 2021-01-05 00:01

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

5条回答
  •  挽巷
    挽巷 (楼主)
    2021-01-05 00:43

    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?

    1. The outermost parentheses put the whole thing in a subshell; this constrains the scope of variables, for purposes of hygeine
    2. We create a temporary file, using a syntax which works with both GNU and BSD mktemp, and call it PIDFILE
    3. We set up a catch-all exit trap (which runs when the outermost subshell exits) to remove the file named by PIDFILE, again for hygeine
    4. We run foo; this is done in a compound statement so that & binds to foo and not to the whole preceding pipeline
    5. We redirect foo's standard error into a process substitution which waits for bar to appear and then kills foo (of which more later)
    6. We capture 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:

    1. First, 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 error
    2. Send the copy of the input going to a file going to another process substitution (a process substitution within a process substitution)
    3. Within the deeper process substitution, firstly run grep -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 ...
    4. kill the process whose PID is captured in the file named by PIDFILE, namely foo

提交回复
热议问题