19

How can I make use of grep in cygwin to find all files that contain BOTH words.

This is what I use to search all files in a directory recursively for one word:

grep -r "db-connect.php" .

How can I extend the above to look for files that contain both "db-connect.php" AND "version".

I tried this: grep -r "db-connect.php\|version" . but this is an OR i.e. it gets file that contain one or the other.

Thanks all for any help

Abs
  • 56,052
  • 101
  • 275
  • 409
  • You could just do two greps. One for one word and one for the other and then find the intersection of matched files. It's not *that* trivial but I don't know if there's a better way of doing it. – Noufal Ibrahim Nov 25 '10 at 10:19
  • Possible duplicate of [How to use grep to match string1 AND string2?](https://stackoverflow.com/q/4487328/608639) – jww Mar 13 '19 at 04:47

8 Answers8

22
grep -r db-connect.php . | grep version
Kevin
  • 53,822
  • 15
  • 101
  • 132
moinudin
  • 134,091
  • 45
  • 190
  • 216
4

If you want to grep for several strings in a file which have different lines, use the following command:

grep -rl expr1 | xargs grep -l expr2 | xargs grep -l expr3

This will give you a list of files that contain expr1, expr2, and expr3.

Note that if any of the file names in the directory contains spaces, these files will produce errors. This can be fixed by adding -0 I think to grep and xargs.

Alexander McNulty
  • 870
  • 1
  • 11
  • 19
Coroos
  • 371
  • 2
  • 9
  • In order to use null delimiters to handle filenames with whitespace characters: `grep -rl -Z expr1 | xargs -0 grep -l -Z expr2 | xargs -0 grep -Z -l expr3`. – Dennis Williamson Jan 18 '14 at 20:32
3

In my cygwin the given answers didn't work, but the following did:

grep -l firststring `grep -r -l secondstring . `
incircuitous
  • 131
  • 6
3

grep "db-connect.php" * | cut -d: -f1 | xargs grep "version"

I didn't try it in recursive mode but it should be the same.

jzrk
  • 285
  • 1
  • 2
  • This doesn't return files where both strings are in, it returns it for one or the other. Right? – Abs Nov 25 '10 at 10:32
  • 1
    Well, no, it should return all files containing the two strings (thoses strings being on different lines). Try : grep "db-connect.php" * | cut -d: -f1 | xargs grep "version" | cut -f1 -d: to have only the file names... – jzrk Nov 25 '10 at 10:36
  • That last command is useful, helped confirm the files I needed to check. So thank you. +1. – Abs Nov 25 '10 at 10:44
  • had a similar case and this worked perfectly except when the first grep only returned 1 file, then didn't get a file name from the xargs grep just the 2nd string – lathomas64 Jun 10 '11 at 15:37
3

To and together multiple searches, use multiple lookahead assertions, one per thing looked for apart from the last one:

instead of writing

grep -P A  * | grep B

you write

grep -P '(?=.*A)B' *

grep -Pr '(?=.*db-connect\.php)version' .

Don’t write

grep -P 'A.*B|B.*A' *

because that fails on overlaps, whereas the (?=…)(?=…) technique does not.

You can also add in NOT operators as well. To search for lines that don’t match X, you normally of course use -v on the command line. But you can’t do that if it is part of a larger pattern. When it is, you add (?=(?!X).)*$) to the pattern to exclude anything with X in it.

So imagine you want to match lines with all three of A, B, and then either of C or D, but which don’t have X or Y in them. All you need is this:

grep -P '(?=^.*A)(?=^.*B)(?=^(?:(?!X).)*$)(?=^(?:(?!Y).)*$)C|D' *

In some shells and in some settings. you’ll have to escape the ! if it’s your history-substitution character.

There, isn’t that pretty cool?

tchrist
  • 78,834
  • 30
  • 123
  • 180
1

Do you mean "string1" and "string2" on the same line?

grep 'string1.*string2'

On the same line but in indeterminate order?

grep '(string1.*string2)|(string2.*string1)'

Or both strings must appear in the file anywhere?

grep -e string1 -e string2
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
1

The uses PCRE (Perl-Compatible Regular Expressions) with multiline matching and returns the filenames of files that contain both strings (AND rather than OR).

grep -Plr '(?m)db-connect\.php(.*\n)*version|version(.*\n)*db-connect\.php' .
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
0

Why to stick to only grep:

perl -lne 'print if(/db-connect.php/&/version/)' *
Vijay
  • 65,327
  • 90
  • 227
  • 319