How to find files containing two strings together in Linux?
I want to find files containing two strings together, for example the file contains both string1
and string2
.
I want the full path of files in output. I don't want to see "permission denied" warnings.
grep -l string2 `grep -l string1 /path/*`
which is the same as
grep -l string2 $(grep -l string1 /path/*)
Edit: heres why grep string1 /path/* | grep string2
doesn't do what I think alwbtc wants.
$ cd /tmp
$ cat a
apples
oranges
bananas
$ cat b
apples
mangoes
lemons
$ cat c
mangoes
limes
pears
$ cd ~
$ grep apples /tmp/* | grep mangoes
$
Nothing found, but file b contains both strings.
Here's what I think alwbtc wants
$ grep -l apples $(grep -l mangoes /tmp/*)
/tmp/b
Here's the equivalent ack command to RedGrittyBrick's answer:
ack string2 $(ack string1 -l)
Works the same way (except ack
by default searches the current directory recursively). The contents within the $()
searches for string1
but -l
outputs only the filenames where that string was found. They are then passed as arguments into the outer command, which means string2
is searched for within those list of files only.
I was looking for an extensible way to do 2 or more strings and came up with this:
grep -rl string1 path-to-files | xargs grep -l string2 | xargs grep -l string3
The first grep recursively finds the names of files containing string1
within path-to-files
.
The results are piped into xargs
which runs one or more grep commands on those files for string2
.
The results are then piped into another xargs
command for string3
- it's the same as the first xargs
call, but looking for a different string.
The use of xargs
will avoid problems where there are so many results that the resultant command line from the use of back-ticks is too long.
To avoid the warnings we can redirect stderr
to /dev/null
:
grep -rl string1 path-to-files 2>/dev/null | xargs grep -l string2
It's not needed on the subsequent grep calls because string1
has already been found inside the file so the permissions are known to be good.
Pipe one grep
into another:
grep "string1" /path/to/files/* | grep "string2"