60

When using find, how do I return the file name and the line number when searching for a string? I manage to return the file name in one command and the line numbers with another one, but I can't seem to combine them.

File names: find . -type f -exec grep -l 'string to search' {} \;

Line numbers: find . -type f -exec grep -n 'string to search' {} \;

Danny_Joris
  • 703
  • 1
  • 5
  • 5

3 Answers3

75

The command line switch -H forces grep to print the file name, even with just one file.

% grep -n 7 test.in
7:7
% grep -Hn 7 test.in
test.in:7:7

   -H, --with-filename
          Print the filename for each match.

Note that as Kojiro says in a comment, this is not part of the POSIX standard; it is in both GNU and BSD grep, but it's possible some systems don't have it (e.g. Solaris).

Kevin
  • 40,767
26

Unless you use the non-standard -H or -r/-R options, grep only outputs the file name if passed more than one file name, so you can do:

find . -type f -exec grep -n 'string to search' /dev/null {} +

With the {} + syntax, find will pass as many files as needed to grep, while with {} ';', it runs one grep per file which is inefficient.

We still add /dev/null (which is guaranteed to exist and be empty) which makes sure grep gets at least two files, for the cases where there's only one regular file in the current directory or below or the list of files has been split by find in such a way that the last run has only one file.

As a side note, with -exec grep regex {} +, find's exit status will be non-zero if any of the grep invocations terminated with a non-zero exit status. It will also be non-zero if find fails to traverse/read some directories or get file metadata. If find returns with a zero exit status, that means find could traverse all directories and all grep invocation succeeded (found at least one file with at least one match), but if it returns non-zero, that doesn't necessarily mean no match was found.

With -exec grep regex {} ';', grep's exit status is only used to determine whether the -exec predicate (which could be used as a condition) succeeds, it's not reflected in find's exit status. So find's exit status will only be about whether it could find all the files.

2

If your grep supports the recursive -r flag, this solves your request:

grep -rn "String to search " * 
Jay
  • 31