find and echo file names only with pattern found

find . -name '*.py' -exec grep something {} \; -print

would print the file name after the matching lines.

find . -name '*.py' -exec grep something /dev/null {} +

would print the file name in front of every matching line (we add /dev/null for the case where there's only one matching file as grep doesn't print the file name if it's passed only one file to look in. The GNU implementation of grep has a -H option for that as an alternative).

find . -name '*.py' -exec grep -l something {} +

would print only the file names of the files that have at least one matching line.

To print the file name before the matching lines, you could use awk instead:

find . -name '*.py' -exec awk '
  FNR == 1 {filename_printed = 0}
  /something/ {
    if (!filename_printed) {
      print FILENAME
      filename_printed = 1
    }
    print
  }' {} +

Or call grep twice for each file - though that'd be less efficient as it would run at least one grep command and up to two for each file (and read the content of the file twice):

find . -name '*.py' -exec grep -l something {} \; \
                    -exec grep something {} \;

In any case, you don't want to loop over the output of find like that and remember to quote your variables.

If you wanted to use a shell loop, with GNU tools:

find . -name '*.py' -exec grep -l --null something {} + |
   xargs -r0 sh -c '
     for file do
       printf "%s\n" "$file"
       grep something < "$file"
     done' sh

(also works on FreeBSD and derivatives).


If you're using GNU grep, you can use its -r or --recursive option to do this simple find for you:

grep -r --include '*.py' -le "$regexp" ./ # for filenames only
grep -r --include '*.py' -He "$regexp" ./ # for filenames on each match

You only need find if you need more advanced predicates.


You can tell grep to include the filename in the output. So if there is a match it will be shown on the console; if there is no match within a file, no line will be printed for that file.

find . -name "*.py" | xargs grep -n -H something

From the man grep:

-H       Always print filename headers with output lines
-n, --line-number
         Each output line is preceded by its relative line number in the file, starting at line 1.  The line number counter is reset for each file processed.
         This option is ignored if -c, -L, -l, or -q is specified.

If your files might have names with spaces in them, you have to switch the pipe to use NUL Characters as a seperator. The full command will now look like this:

find . -name "*.py" -print0 | xargs -0 grep -n -H something

Tags:

Grep

Find