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