Nested piped grep results in a string saying "(standard input)"

The -l option to grep will make the utility print only the name of the file containing the specified pattern. The manual on my system says the following about this option:

Only the names of files containing selected lines are written to standard output. grep will only search a file until a match has been found, making searches potentially less expensive. Pathnames are listed once per file searched. If the standard input is searched, the string "(standard input)" is written.

Since the second grep in your pipeline is reading from standard input, not from a file, it is unaware of where the data is coming from other than that it's arriving on its standard input stream. This is why it's returning the text string (standard input) (not (standard output) as written in the question). This is as close as it is able to get to where the match was located.

To combine the two patterns in the first grep (which does have knowledge about what files it's looking in), see "How to run grep with multiple AND patterns?"


use "cut" to remove strings after ":" then you get file parts (assuming file paths don't contain colon or newline characters and don't match the second pattern themselves).

grep -ir "Some string" . |grep "Another string I want to find in the other grep's results" | cut -d ":" -f 1

in case of duplications use "uniq"

grep -ir "string1" . | grep "string2" | cut -d: -f1 | uniq

(I'm assuming you intended the second grep to match on the content of line and not on the file names or both as your approach was doing)

POSIXly:

find . -type f -exec awk '
  FNR == 1 {found = 0}
  !found && tolower($0) ~ /some string/ && /other string/ {
    print FILENAME
    found = 1
    nextfile
  }' {} +

The business about found is for awk implementations that don't support nextfile yet (and where nextfile is then a no-op). If you know your awk implementation supports nextfile, you can simplify it to:

 find . -type f -exec awk 'tolower($0) ~ /some string/ && /other string/ {
    print FILENAME; nextfile}' {} +

With GNU grep built with PCRE support, since you want one match to be case insensitive and not the other:

grep -rlP '^(?=.*(?i:some string))(?=.*other string)' .

(?=...) is a perl look-ahead operator. (?i:pattern) turns on case-insensitive match for pattern only. So here we're matching on the beginning of the line (^) provided it's followed by any number of characters (.*) followed by some string (case insensitive) and also that it (the beginning of the line) is followed by any number of characters and other string (case sensitive).

If your grep doesn't support -P, you may have the pcregrep command instead (replace grep -rlP with pcregrep -rl), or if the patterns don't overlap, you could do:

grep -rl -e '[sS][oO][mM][eE] [sS][tT][rR][iI][nN][gG].*other string' \
         -e 'other string.*[sS][oO][mM][eE] [sS][tT][rR][iI][nN][gG]' .

Or if you don't care both matches being case insensitive:

grep -ril -e 'some string.*other string' \
          -e 'other string.*some string' .

Tags:

Grep

Pipe