41

Is there a grep-like utility that will enable me to do grep searches with logic operators. I want to be able to nest and combine the logical constructs freely. For example, stuff like this should be possible:

grep (term1 && term2) || (term1 && (term3 xor term4)) *

I realize this can be done with vanilla grep and additional bash scripting, but my goal here is to avoid having to do that.

6 Answers6

63

There are lot of ways to use grep with logical operators.

  1. Using multiple -e options matches anything that matches any of the patterns, giving the OR operation.

    Example: grep -e pattern1 -e pattern2 filename

  2. In extended regular expressions (grep -E), you can use | to combine multiple patterns with the OR operation.

    Example: grep -E 'pattern1|pattern2' filename

  3. grep -v can simulate the NOT operation.

  4. There is no AND operator in grep, but you can brute-force simulate AND by using multiple patterns with |.

    Example : grep -E 'pattern1.*pattern2|pattern2.*pattern1' filename

    The above example will match all the lines that contain both pattern1 and pattern2 in either order. This gets very ugly if there are more patterns to combine.

ilkkachu
  • 138,973
Thushi
  • 9,498
16

With awk, as with perl, you'll have to wrap terms in //, but it can be done:

awk '(/term1/ && /term2/) || (/term1/ && xor(/term3/, /term4/))' 

Note that xor() which is a non-standard GNU extension is a bitwise xor, not a logical one. It's fine here as those /regex/ only ever return 0 or 1.

With other awk's you can define a logical xor() as a function:

function xor(a, b) {
  return (a || b && ! (a && b))
}

Or

function xor(a, b) {
  return ((a && !b) || (b && !a))
}
muru
  • 72,889
9

You could use perl:

perl -wne 'print if (/term1/ && /term2/) || (/term1/ && (/term3/ xor /term4/))'

Where the switches are as follows:

-w turns on warnings
-n runs perl against each line of input (looping)
-e executes perl passed in the command line
user1794469
  • 4,067
  • 1
  • 26
  • 42
michas
  • 21,510
4

GNU grep allows perl regex, so we can do this to find lines containing both word1 and word2.

grep -P '^(?=.*word1)(?=.*word2)' filename

(see this post) for more details.

sgu
  • 141
2

I use to chain grep commands to achieve a logical AND:

grep expr1 filename | grep expr2

I believe it is pretty straightforward, Unix-like and elegant.


Then you can combine (as @Tushi thoroughly explained) with the -E option for OR-ing and -v for negating.

Your specific example is pretty nasty and probably would benefit from some more powerful utility (see @muru's answer).

Campa
  • 131
1
sed -e '/term1/!d;/term2/b' -e '/term3/!d;/term4/d' -- *

I believe that accomplishes what you're trying to do. It deletes from output any line which doesn't match term1, it branches out of the script (and so autoprints) any line that remains and that matches term2, and for lines that remain it deletes any which do not match term3 and from those any that do match term4.

sed scripts are evaluated in order, and all tests are boolean, so any actions resulting from a test are going to directly affect the behavior of any following actions.

mikeserv
  • 58,310