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.
7 Answers 7
There are lot of ways to use grep
with logical operators.
Using multiple
-e
options matches anything that matches any of the patterns, giving the OR operation.Example:
grep -e pattern1 -e pattern2 filename
In extended regular expressions (
grep -E
), you can use|
to combine multiple patterns with the OR operation.Example:
grep -E 'pattern1|pattern2' filename
grep -v
can simulate the NOT operation.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.
-
9
grep foo | grep bar
is a more general way to do AND.Kenster– Kenster2015年01月05日 15:43:06 +00:00Commented Jan 5, 2015 at 15:43 -
4+1 for clearly explaining that there isn't a real "AND" operator, and that the best we can do is simulate a hack using an OR structure. This will of course get unwieldy with more than 3 terms, but for two terms it works well.Eric Hepperle - CodeSlayer2010– Eric Hepperle - CodeSlayer20102017年04月20日 15:57:39 +00:00Commented Apr 20, 2017 at 15:57
-
grep -c -e MATCH1 -e MATCH2 is great when you are interested in filename counts.andrej– andrej2019年07月10日 11:26:54 +00:00Commented Jul 10, 2019 at 11:26
With awk
, as with perl
, you'll have to wrap terms in //
, but it can be done:
awk '(/term1/ && /term2/) || (/term1/ && xor(/term3/, /term4/))'
and we could factor out term1
to have:
awk '/term1/ && (/term2/ || 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))
}
Or
function xor(a, b) {
return (a ? !b : b)
}
or we could just write the condition without a function:
awk '/term1/ && (/term2/ || (/term3/ ? !/term4/ : /term4/))'
-
Or
... /term3/+/term4/==1
dave_thompson_085– dave_thompson_0852024年11月08日 01:38:22 +00:00Commented Nov 8, 2024 at 1:38
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
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.
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).
sed -e '/term1/!d;/term2/b' -e '/term3/!d;/term4/d' -- *
I believe that accomplishes what you're trying to do. It d
eletes from output any line which doesn't match term1
, it b
ranches 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.
To achieve AND in grep, I hacked a "grepand" tool which creates a chain of "| grep ..." commands; and it accomodates for the "-v" flag:
$ cat grepand #!/bin/sh test $# -eq 0 && cat && exit 0 unset V COMMAND=`for i; do test $i = "-v" && V="-v" && continue echo -n "|grep -E $V -i $i "; unset V done |cut -c 2-` eval $COMMAND
-
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.2025年05月29日 06:06:45 +00:00Commented May 29 at 6:06
-
This has several logical and quoting-related issues. It would not be able to look for the string
-v
or any pattern containing a space or tab, and might have issues with patterns looking like filename globbing patterns, shell expansions, general shell code (redirections, pipes etc.)2025年05月29日 06:16:10 +00:00Commented May 29 at 6:16