I've got a program (prog) that takes a lot of time to produce its output.

I need to check if the output contains string1 and not string2.

Right now I do the following. It invokes prog 2 times.

if   prog | grep -q 'some string' &&
   ! prog | grep -q 'another string'; then
    echo 'OK'
else
    echo 'Not OK'
fi

Is there are way to do this with only 1 invocation of prog?

Any solution involving awk or sed will do as well.

UPDATE

What I was hoping for was a one liner—that my mind cannot see—to do the trick, having GNU coreutils at my disposal.

3 Answers  正确答案

I don't think it is possible with grep without executing prog twice, or better, saving it's output into a temporary file.

I would recommend to use awk:

awk '/string1/{a=1}/string2/{b=1}END{exit !a && b}'

You can use it in the shell script like:

if prog | awk '/string1/{a=1}/string2/{b=1}END{exit !a && b}' ; then
    echo "ok"
else
    echo "not ok"
fi

Note: awk treats 0 as false and 1 as true. The shell treats 0 as true and 1 as false. To reflect that we return !a && b instead of the a && !b, which might look more reasonable in the first place.

  • 1
    Got it. I need to learn more awk. – totoro May 29 '16 at 12:14
  • 1
    Especially if the condition gets more complicated awk comes in handy. – hek2mgl May 29 '16 at 12:27
  • 1
    It's simpler to think of the shell as dealing with success and failure, not boolean values. 0 indicates success, non-zero indicates failure. – chepner May 29 '16 at 14:57
  • In my case I had to do exit a && b. Or inspired by @chepner: awk 'BEGIN{a=1;b=0}/string1/{a=0}/string2/{b=1}END{exit a + b}'. – totoro May 29 '16 at 18:37

With sed, to output line that meet the requirements :

prog | sed -n ':a;$!N;s/\n//;ta;/string1/{/string2/!p}'

Update :

To test the sed command (GNU sed) :

prog | sed -n ':a;$!N;s/\n//;ta;/string1/{/string2/!{q};/string2/{q 1}}' && echo "ok" || echo "nok"
  • This operates just on a single line, not on the whole output. – hek2mgl May 29 '16 at 12:28
  • @hek2mgl Updated for multiline output. – SLePort May 29 '16 at 13:09
  • Having that you have string1 in $1 and string2 in $2, this will do as well: sed -n "/$1/H;/$2/H;\${x;/$1/{/$2/q1}}" file (with GNU sed) I'm using the hold buffer for matching lines only instead of buffering the whole file. – hek2mgl May 29 '16 at 13:18 
  • Actually I don't need to use the hold buffer. This should do as well: sed -n "/$1/!d;/$2/!d;N;\${/$1/{/$2/q1}}" file – hek2mgl May 29 '16 at 13:20 
  • @hek2mgl I tried sed -n "/foo/!d;/bar/!d;N;\${/foo/{/bar/q1}}" <<< "foo, bar"; echo $?, it returns 0. Something I missed ? – SLePort May 29 '16 at 13:31 

For the problem by using grep command :

prog | grep -E 'string1|string2' | tr -d "\n" | grep  -v string2 | grep string1 && echo "OK" || echo "NOT OK" 

来自  https://stackoverflow.com/questions/37509373/check-if-file-contains-string1-and-not-string2