Bash – How to grep a program’s output but also echo the output normally

bashgrepio-redirectionpipe

I'm working with a program that outputs error messages when something goes wrong, but does not set its exit status accordingly: the exit status is always 0, indicating success. I'd like to run this program from a shell script and get a non-zero exit status if it emits any error messages, so the script can tell that the program failed.

The error messages follow a predictable pattern that I can match with grep, and grep sets its exit status based on whether it found a match, so I can pipe the program's output into grep and negate the result with ! to get the exit status I want.

The problem is that if I pipe into grep, I can't see the program's output anymore, because grep consumes it. I'd like to somehow scan the output for error messages but also display the output normally, since there are other important messages besides the errors. Unfortunately, grep doesn't have an option to pass-through all of its input, matching and non-matching lines alike.

One approach I've found that mostly works is:

! my_program | tee /dev/stderr | grep -q "error message"

This feeds the program's output into grep but also copies it to standard error, which isn't redirected, so I can see it. That's OK when my script's standard output and standard error both go to a terminal (so it doesn't matter which one gets the messages), but it can cause problems if standard out and standard error are redirected to different places; the messages will end up in the wrong place.

Using bash or POSIX shell, and GNU tools, is there a (concise) way to scan a program's standard out and/or standard error streams with something like grep and set exit status accordingly, but also let both streams go to their normal destinations?

(Note that my_program writes its error messages to standard out, not standard error, so a solution that can only scan the standard output stream is OK, though not ideal. And in case it makes a difference, I'm doing this on CentOS 7.)

Best Answer

...is there a (concise) way to scan a program's standard out and/or standard error streams with something like grep and set exit status accordingly, but also let both streams go to their normal destinations?

...my_program writes its error messages to standard out, not standard error, so a solution that can only scan the standard output stream is OK, though not ideal.

My solution answers the bolded portion above.

I think the easiest way to do this is through awk:

myprogram |
awk 'BEGIN {status = 0} /error message/ {status = 1} 1; END {exit(status)}'

The awk command outputs everything it inputs exactly as-is, but at the end it exits with a status dependent on whether "error message" was a part of the input.

Concise version (with a shorter variable name):

myprogram | awk 'BEGIN{s=0} /error message/{s=1} 1; END{exit(s)}'
Related Question