So for the longest time I was always bothered by the fact that I couldn’t grep for files of a particular name that were buried several levels deep. For example, if you wanted to do grep -r nifty.java.property *.properties
, then unless you had a *.properties file in your current directory, grep would assume that there are no other matches lower down in the structure, so you’d find nothing. Hmph.
So of course, find comes to the rescue. You can run find . -name *.properties -exec grep nifty.java.property {} \;
and that finds all your matches – with one problem: this doesn’t tell you which file it found the match in, so that’s pretty useless as it stands (in any case, remember to escape the ; with \ if you run this from the shell to prevent your shell from interpreting the ; as a command separator and not passing it to find).
So, there are a couple of solutions to this.
find . -name *.properties -exec grep nifty.java.property /dev/null {} \;
The reason why this works: grep normally only shows the file names in which it matched a string if it has multiple files to process. Since the find command launches one grep per file name matched when it is ended with ; (in this case \;, otherwise the shell would interpret it instead of it being passed to find), grep assumes which file name you just called it with and therefore doesn’t spit out the file name.
So, adding the /dev/null means another file to be searched (which is always going to return 0 results), therefore grep decides it needs to show the file name, which is really cool, because otherwise find gives you no clue as to which file it executed grep for. You could of course pass -print to find, but that gives you every single file name matched and would be totally useless as you would potentially drown in matches.
This is probably the most portable option since it does not rely on any special options for grep or find.find . -name *.properties -exec grep nifty.java.property {} +
For the longest time I totally missed that most (if not all) find versions offer the option of ending the command with +. This has the effect of passing as many paths as will fit as an argument, i.e. we’re back to grep having multiple files to match and it prints the path name again. Neat!find . -name *.properties -exec grep -H nifty.java.property {} \;
Some versions of grep offer the -H option, which forces grep to act as if it was matching multiple files and it prints out the file name (with full path) once again.
So there you have it – three methods of getting the results you need. Happy grepping! ;]
PS: If find barfs on a whole bunch of “permission denied”‘s and spews out a bunch of extra stuff that prevents you from easily seeing the results you’re interested in, remember that you can add a 2> /dev/null
to redirect stderr to that great bit-bucket in the sky and get rid of all that annoying stuff.
PPS: Of course if you have bash file globbing turned on and at least Bash 4 (and who doesn’t these days), then you can also simply do grep nifty.java.property **/*.properties
, which is probably the simplest solution all around.
PPPS: These days I tend to go with The Silver Searcher (aka ag
) which has a LOT more nifty features, especially if you’re mostly searching through code (e.g. automatically ignore .git
directories, etc., etc.