26

I am trying to build a grep search that searches for a term but exludes lines which have a second term. I wanted to use multiple -e "pattern" options but that has not worked.

Here is an example of a command I tried and the error message it generated.

grep -i -E "search term" -ev "exclude term"
grep: exclude term: No such file or directory

It seams to me that the -v applies to all search terms / patterns. As this runs but then does not include search term in results.

grep -i -E "search term" -ve "exclude term"
  • Is there any other option for exclude, as sometimes we have to grep lines around a word and If we exclude in the next operation using '|' , it just removes that word but doesn't remove the block for that word – Learner Dec 6 '18 at 17:12
39

To and expressions with grep you need two invocations:

grep -Ei "search term" | grep -Eiv "exclude term"

If the terms you are searching for are not regular expressions, use fixed string matching (-F) which is faster:

grep -F "search term" | grep -Fv "exclude term"
14

Short of invoking grep twice, there is only one way I can think of to accomplish this. It involves Perl Compatible Regular Expressions (PCRE) and some rather hacky look-around assertions.

To search for foo excluding matches that contain bar, you can use:

grep -P '(?=^((?!bar).)*$)foo'

Here's how it works:

  • (?!bar) matches anything that not bar without consuming characters from the string. Then . consumes a single character.

  • ^((?!bar).)* repeats the above from the start of the string (^) to the end of it ($). It will fail if bar is encountered at any given point, since (?!bar) will not match.

  • (?=^((?!bar).)*$) makes sure the string matches the previous pattern, without consuming characters from the string.

  • foo searches for foo as usual.

I found this hack in Regular expression to match string not containing a word?. In Bart Kiers' answer, you can find a much more detailed explanation of how the negative look-ahead operates.

10

If you want to do this in one pass, you can use awk instead of grep.

Format:

echo "some text" | awk '/pattern to match/ && !/pattern to exclude/'

Examples:

  • echo "hello there" | awk '/hello/ && !/there/'

Returns nothing.

  • echo "hello thre" | awk '/hello/ && !/there/'

Returns: hello thre

  • echo "hllo there" | awk '/hello/ && !/there/'

Returns nothing.

For multiple patterns, you can use parenthesis to group them.

Examples:

  • echo "hello thre" | awk '(/hello/ || /hi/) && !/there/'

Returns: hello thre

  • echo "hi thre" | awk '(/hello/ || /hi/) && !/there/'

Returns: hi thre

  • echo "hello there" | awk '(/hello/ || /hi/) && !/there/'

Returns nothing.

  • echo "hi there" | awk '(/hello/ || /hi/) && !/there/'

Returns nothing.

  • 1
    It worked for me, but I lost the colors =P – Leopoldo Sanczyk Aug 26 '17 at 23:38
  • 1
    Colors from what output? If you are trying to preserve colors with ls, use the "--color=always" argument whenever parsing the output (or you will normally always lose the colors when parsing the text). Example: ls --color=always | awk '/hello/ && !/goodbye/' – Philip Reese Sep 4 '17 at 9:27
  • Thanks for the answer @Philip! I tried that before, but without success. I guess that as the pattern has the colored text, it doesn't match later, and I should include some kind of color code in the pattern. Anyway, yours is the fastest way that I found to do grep -R in several code files using Ubuntu command line. – Leopoldo Sanczyk Sep 5 '17 at 17:19
1

From my experiments it does not seam to make much difference if you pipe your exclude terms through grep or sed. Sed has some other useful text replacement features which I often use to better filter the out put of log files. So I am going to use sed as I combine quite a number of filters on sed.

wc /var/log/tomcat/tomcat.2013-01-14.log.1 
  1851725

 /usr/bin/time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | sed -e "/login OK/d" -e "/Login expired/d" | wc
24.05user 0.15system 0:25.27elapsed 95%CPU (0avgtext+0avgdata 3504maxresident)k
0inputs+0outputs (0major+246minor)pagefaults 0swaps
   5614   91168 1186298

 /usr/bin/time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | sed -e "/login OK/d" -e "/Login expired/d" | wc
23.50user 0.16system 0:24.48elapsed 96%CPU (0avgtext+0avgdata 3504maxresident)k
0inputs+0outputs (0major+246minor)pagefaults 0swaps
   5614   91168 1186298

 /usr/bin/time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | grep -v -e "login OK" -e "Login expired" | wc
23.08user 0.14system 0:23.55elapsed 98%CPU (0avgtext+0avgdata 3504maxresident)k
0inputs+0outputs (0major+246minor)pagefaults 0swaps
   5614   91168 1186298

 /usr/bin/time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | grep -v -e "login OK" -e "Login expired" | wc
23.50user 0.15system 0:25.27elapsed 93%CPU (0avgtext+0avgdata 3488maxresident)k
0inputs+0outputs (0major+245minor)pagefaults 0swaps
   5614   91168 1186298

  • 3
    Try comparing the runtime of grep -F instead of grep -E and don't use -i if you don't need it. – Thor Jan 17 '13 at 20:17
  • 1
    But then you don't provide examples using sed ;) – Benjamin R Mar 7 '18 at 3:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.