How to use grep to find all files that contain one pattern but don't have a second?

StackOverflow https://stackoverflow.com/questions/1422292

  •  07-07-2019
  •  | 
  •  

Question

Is there an easy way using grep (or combine with other standard command line tools) to get a list of all files which contain one pattern yet don't have a second pattern?

In my specific case I want a list of all files that contain the pattern:

override.*commitProperties

yet don't contain:

super.commitProperties

I'm on Windows but use cygwin extensively. I know how to either find all files with the first pattern OR all patterns without the second, but I'm not sure how to combine those queries.

I'd prefer answers that are generic, as I have a feeling plenty of other people could find this type of query useful. It's easy enough for me to take a generic solution and plug in my values. I just included my specific instance to make the explanation easier.

Was it helpful?

Solution

grep -rl "override.*commitProperties" . | xargs grep -L "super.commitProperties"

-l prints the files with a match

-L prints the files without a match

OTHER TIPS

Try

find . -print0 | xargs -0 grep -l "override.*commitProperties" \
| tr '\012' '\000' | xargs -0 grep -L super.commitProperties

The tr command will convert newlines to ascii null, so that you can use -0 in the second xargs, avoiding all issues with spaces in file names etc.

Test result:

/tmp/test>more 1 2 3 | cat
::::::::::::::
1
::::::::::::::
override.*commitProperties
super.commitProperties
::::::::::::::
2
::::::::::::::
override.*commitProperties
::::::::::::::
3
::::::::::::::
hello world
/tmp/test>find . -print0 | xargs -0 grep -l "override.*commitProperties" | tr '\012' '\000' | xargs -0 grep -L super.commitProperties
./2
/tmp/test>

As noted by Douglas, find+xargs can be be substituted by grep -r.

Use a combination of 2 greps and a comm, as follows (patterns are A and B). Please note that a piped grep won't work since the patterns could be on disparate lines.

$ cat a
A
$ cat b
B
$ cat ab
A
B

$ grep -l A * > A.only
$ grep -l B * > B.only  

$ comm -23 A.only B.only 
a

NOTE: comm command prints the lines common to or unique to two files. "-23" prints lines unique to first file - thus suppressing filenames in second file.

My solution is similar to Douglas Leeder's, except I don't use xargs:

grep -l 'override.*commitProperties' $(grep -L super.commitProperties *.txt)

The grep -L command product a list of files that DOES NOT contain the pattern super.commitProperties, the grep -l command looks for the *override.commitProperties pattern off that list.

Overall, it is a different way to skin the cat.

ack -l --make "override.*commitProperties" | xargs ack -L "super.commitProperties"

I used this thread and was trying to do a recursive lookup. It was taking 25 minutes+ so found out about ack instead. Did it in 5 minutes.

Handy tool ack.

    grep "override.*commitProperties" *| grep -v "super.commitProperties" | cut -d":" -f1
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top