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
$ cat b
$ cat c
$ 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/*)
| improve this answer | |
  • 1
    This is a neat solution and more useful than mine. For those that want to know what is going on here (it took me a bit to figure out), he's using the -l option to return the file names instead of the lines. He's then using the dollar sign or the back quotes to pass that list as the FILE argument into the second grep. This allows the second grep to search the entirety of each file found instead of the individual lines as in my solution. – embedded.kyle Jun 18 '12 at 14:12
  • You missed a -l option for both commands to be considered equal. – pong Sep 11 '13 at 11:12

Pipe one grep into another:

grep "string1" /path/to/files/* | grep "string2"

| improve this answer | |
  • 6
    If the two strings are on different lines in the file, this wont work. – RedGrittyBrick Jun 15 '12 at 20:01
  • 1
    I didn't know there was a requirement for them to be in the same line @RedGrittyBrick – slhck Jun 15 '12 at 21:35
  • @slhck: I've updated my answer to show what I think alwbtc wants and why this answer doesn't do that. Of course, I may have misunderstood what alwbtc wants and embedded.kyle may have got it right - I suspect not though. – RedGrittyBrick Jun 15 '12 at 22:28

Here's the equivalent 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.

| improve this answer | |

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.

| improve this answer | |
  • Nice solution. Especially if you work with a large data set and want to see results fast. But if you have white space in your filenames, add something like -d '\n' to xargs. – Robert Aug 10 at 12:39
  • 1
    If the filenames contain white-space it may be better to use -Z or --null as an argument to the grep calls and -0 (dash zero) to xargs. Filesnames are not allowed to contain the null character so it's a safe delimiter. – awatts Aug 10 at 13:51
comm -12 <(grep --fixed-strings --files-with-matches "STRING1" /path/to/files/* 2>/dev/null | sort) <(grep --fixed-strings --files-with-matches "STRING1" /path/to/files/* 2>/dev/null | sort)

or less redundantly:

search_files () { str="$1"; shift; grep -Fl "$str" "$@" 2>/dev/null | sort; }
comm -12 <(search_files "STRING1" /path/to/files/*) <(sf "STRING2" /path/to/files/*)

This will work if the strings are on different lines of the same file and will also avoid false positives if a filename contains one of the strings.

| improve this answer | |

To elaborate on @RedGrittyBrick's solution which has a shortcoming when running the command unattended plus to suppress error output as intended and to find files recursively you might consider

grep -l 'STRING1' $(! grep -lrs 'STRING2' /absolute/path/to/search/dir && echo /dev/null)

-s option will suppress error messages
-r option allows to search for strings in arbitrarily nested directories
! combined with && echo /dev/null guarantees that the command won't hang up. Otherwise, if the inner grep doesn't find any file it won't output anything so that the outer grep will indefinitely wait for input to search upon. This solution outputs /dev/null in these cases so outer grep will search for STRING1 in /dev/null where it's supposed to not finding anything.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.