Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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"
share|improve this question
    
Whenever I search history | grep sed to retrieve some complicated cmdline, I always have at least one result... –  gerrit Jan 17 '13 at 17:02
add comment

3 Answers

up vote 15 down vote accepted

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"
share|improve this answer
add comment

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.

share|improve this answer
add comment

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

share|improve this answer
2  
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
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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