20

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?

  • 2
    @Downvoter: Happy to improve this question if you can share your concerns. – Dancrumb Aug 16 '17 at 16:41
  • 2
    many find versions have xargs built in: find . -type f -exec fgrep <search_string> {} + – simpleuser Aug 16 '17 at 22:53
41

Check if your grep supports -r option (for recurse):

grep -r <search_string> .
  • 1
    Yup... I just found stackoverflow.com/questions/16956810/… and that's the answer there too. – Dancrumb Aug 16 '17 at 16:31
  • add a comment about --exclude-dir to address performance and we have a winner! – Dancrumb Aug 16 '17 at 16:35
  • 1
    Just notice that this is not portable, however grep on recent FreeBSD and Linux distros support it. And why --exclude-dir? Didn't you ask to search a whole tree? – Philippos Aug 16 '17 at 16:39
  • Fair point... --exclude-dir is actually handy in my use case (because parts of the subtree are large, but useless) and I did ask about performance... but you're right, it's not necessary. – Dancrumb Aug 16 '17 at 16:40
  • In this case I have to add that IIRC --exclude-dir is exclusive to GNU grep. (-: – Philippos Aug 16 '17 at 16:45
12

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.

  • 7
    Use -r instead of -R to skip symlinks when GNU grep is concerned – αғsнιη Aug 16 '17 at 16:43
  • 1
    @AFSHIN Why would you not want to follow symlinks? – Kusalananda Aug 16 '17 at 16:46
  • 4
    @Kusalananda Recursion? Although current GNU grep implementations catch recursions, I think. Otherwise it depends on what you mean by "tree". – Philippos Aug 16 '17 at 16:51
  • 2
    @Philippos IMHO, babysitting the user is not something that a tool like grep should do. If the user has symbolic link loops in their directory structure, well, that's the user's problem :-) – Kusalananda Aug 16 '17 at 18:37
  • 3
    @Kusalananda And if the system provided the loop? Never got lost in /sys/devices/cpu/subsystem/devices/cpu/subsystem/devices/cpu/... (-X I like tools babysitting me (unless they provide weird magic they call "AI"). (-; – Philippos Aug 16 '17 at 19:21
12

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.

ack

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
  • 5
    ack is great, and a faster version of ack is ag (the silver searcher, geoff.greer.fm/ag) – cfeduke Aug 16 '17 at 20:33
  • 1
    I prefer this with filter like -name '*.log' It is faster. – sdkks Aug 16 '17 at 22:50
  • @cfeduke I haven't tried it, mostly because ag isn't part of default apt repositories on WSL (you gotta work with what you have got !) – Pierre-Antoine Guillaume Aug 17 '17 at 22:03
  • A trick is to add /dev/null to the grep to get the filename to appear. – ChuckCottrill Aug 18 '17 at 1:11
  • A trick is to search only for directories and then -exec grep /dev/null {}/* to get all files with single fork/exec per directory. – ChuckCottrill Aug 18 '17 at 1:12
5

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. :)

0

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"
  • 1
    backtics aren't good modern practice, they are all but obsolete – Christopher Aug 17 '17 at 19:31
  • 1
    This will break if you have files with special characters in their names. I don't know how special they have to be in order to be too special for this to work as-is, but what you are doing is really much the same thing as parsing the output of ls, which is also bad. – a CVn Aug 17 '17 at 19:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.