How do I pass a list of files to grep
Well, the generic case that works with any command that writes to stdout is to use xargs
, which will let you attach any number of command-line arguments to the end of a command:
$ find … | xargs grep 'search'
Or to embed the command in your grep
line with backticks or $()
, which will run the command and substitute its output:
$ grep 'search' $(find …)
Note that these commands don't work if the file names contain whitespace, or certain other “weird characters” (\'"
for xargs, \[*?
for $(find …)
).
However, in the specific case of find
the ability to execute a program on the given arguments is built-in:
$ find … -exec grep 'search' {} \;
Everything between -exec
and ;
is the command to execute; {}
is replaced with the filename found by find
. That will execute a separate grep
for each file; since grep
can take many filenames and search them all, you can change the ;
to +
to tell find to pass all the matching filenames to grep
at once:
$ find … -exec grep 'search' {} \+
Some versions of grep
(e.g. on non-embedded Linux or BSD or Mac OS X) have a -r
option to make a recursive search. On OpenBSD, use -R
(and there's no --exclude
as in the example below). This covers simple combinations of find
with grep
.
If your implementation doesn't have the -R
flag, or if you want fancier file matching criteria, you can use the -exec
primary of find
to make it execute grep
. A few older find
implementations don't support -exec
… +
; on these systems, use a ;
instead of the +
(this will call grep
once per file, so it'll be slower, but otherwise the result will be the same). Note the /dev/null
trick to cause grep
to show the file name even if it happens to be called on a single file (GNU grep and FreeBSD/NetBSD/OSX grep have a -H
option to achieve the same effect).
find . -type f -name '*.o' -prune -o -exec grep 'needle' /dev/null {} +
grep -r --exclude='*.o' 'needle' .