Is there a simpler way to grep all files under a directory?
When I want to search a whole tree for some content, I use
find . -type f -print0 | xargs -0 grep <search_string>
Is there a better way to do this in terms of performance or brevity?
Check if your grep
supports -r
option (for recurse):
grep -r <search_string> .
If you want to recurse down into subdirectories:
grep -R 'pattern' .
The -R
option is not a standard option, but is supported by most common grep
implementations.
A sub optimal answer :
Instead of piping the output of find
into grep
, you could just run
find . -type f -exec grep 'research' {} '+'
and voila, one command instead of two !
explanation :
find . -type f
find all regular files within .
-exec grep 'research'
grep ‘research’
{}
in found filename
'+'
use one command per all the filenames, not once per filename.
Nb : with ';'
it would have been once per filename.
Other than that, if you use that to process source code, you may look into ack
, which is made for looking for code bits easily.
Edit :
You can extend that research a little. First, you can use the -name ''
switch of find
to look for files with specifig naming pattern.
For instance :
-
only files that correspond to logs :
-name '*.log'
-
only files that correspond to c headers, but you can’t stick with uppercase or lowercase for your filename extensions :
-iname *.c
Nb : like for grep
and ack
, the -i
switch means case insensitive in this case.
In that case, grep will show without color and without line numbers.
You can change that with the --color
and the -n
switches (Color and lines numbers in files respectively).
In the end, you can have something like :
find . -name '*.log' -type f -exec grep --color -n 'pattern' {} '+'
for instance
$ find . -name '*.c' -type f -exec grep -n 'hello' {} '+'
./test2/target.c:1:hello
As noted above -r
or -R
(depending on desired symlink handling) is a quick option.
However -d <action>
can be useful at times.
The nice thing about -d
is the skip command, which silences the “grep:
directory_name: Is a directory” when you just want to scan the current level.
$ grep foo *
grep: q2: Is a directory
grep: rt: Is a directory
$ grep -d skip foo *
$
and of course:
$ grep -d recurse foo *
(list of results that don't exist because the word foo isn't in our source code
and I wouldn't publish it anyway).
$
The -d skip
option is REALLY handy inside another script so you don’t have to 2> /dev/null
. 🙂
If you are dealing with a lot of files, the grep runs faster if you prune down the files it needs to search through rather than grepping all files in subfolders.
I use this format sometimes:
grep "primary" `find . | grep cpp$`
Find all files in subfolders of .
that end in cpp
. Then grep those files for “primary”.
If you want, you can keep piping those results into further grep calls:
grep "primary" `find . | grep cpp$` | grep -v "ignoreThis" | grep -i "caseInsensitiveGrep"