5

How do I search files, some of which might be directories (in this case recursively) for a given string?

I'd like to search source files for a class definition, so it would also be great if there was the name of the containing file before each output string.

2
  • May I ask why would you need that?
    – Anonymous
    Jun 29, 2009 at 13:09
  • I'm searching source files for a method definition.
    – Alex
    Jul 1, 2009 at 17:40

9 Answers 9

8

You could use grep:

grep -rn 'classname' /path/to/source

This will also print the line number next to each match.

Case insensitive:

grep -rin 'classname' /path/to/source

Search non-recursively in all cpp files:

grep -n 'classname' /path/to/source/*.cpp
4

All the above answers will work perfectly well, but may cause false positives if you've got the string in more than just source code. Say you are looking for where you use the FooBar class in a java project, you can search for all files named *.java and grep just those.

# grep "FooBar" -Hn $(find -name "*.java")

The section in between the $() will expand to the output of that command. The -H prints out the filename. It's the default where there is more than one file name, but we explicitly add it here in case the expansion only returns one file name. The -n prints the line number of the match.

This will work for small projects, but if you're dealing with a larger project, there the potential for the find to expand to a string longer than the command line limit. It's therefore safer to use:

# find -name "*.java" -print0 | xargs -0 grep "FooBar" -Hn 

xargs will take input from standard in and split it up so it fits on the command line, running the command multiple times if necessary. The -print0 and -0 are required to deal with files with spaces in their name.

You can also look for all the filesnames with a match in using:

# find -name "*.java" -print0 | xargs -0 grep "FooBar" -l

The -l will just display the file name and stop looking in a particular file as soon as it finds a match.

If you're going to be doing this sort of thing frequently, you may want to look at a tool like ctags or exuberant tags, which maintain a search index of your source code and can answer these sorts of questions quickly. Both vim and emacs have support for these tools. You may also like to look at an IDE like eclipse, which can do cross referencing very well.

(I know you were looking for a class definition, but in Java a class will be defined in a file with the same name as the class. If you were doing cpp, you could do something like:

# find -name "*.cpp" -o -name "*.cc" -o -name "*.h" -o -name "*.hpp" -print0 | \
  xargs -0 egrep "class\s+FooBar" -Hn 
1
  • Very comprehensive answer.
    – nickd
    Sep 23, 2010 at 21:20
2

For searching through source code, I've found that ack (betterthangrep.com) is, to quote the marketing, better than grep. Its output is easier to read and it will do a lot of stuff automagically, like ignoring non-source code files: VCS directories (eg CVS and .svn), backup files.

2

ack is better for searching source codes as it already recognize some of the most popular file extension (c, perl, etc). It's as fast as grep and by default ignoring all your version control artifact. For example if you want to search some pattern perl source code you could use

ack --perl pattern

instead of

grep pattern $(find . -name '*.pl' -or -name '*.pm' -or -name '*.pod' | grep -v .svn)
0
grep -r string *

finds all files containing the characters "string" in all (non-hidden) files in the current directory and all sub-directories recursively. You can use the usual regexes to tune the string. See the grep info/man pages for more options.

0

Already answered (I think this is what you are looking for) on this serverfault post:

Recursive text search with grep and file patterns

0

this is something I find myself using alot so I have put two bash functions in my .bashrc
First one does a recursive search for text in filenames.
Second one will do a recursive search for text inside of files returning the filenames of the files that match.

ff () {
        find . -name "*$@*" -print; 
}


fff () {
    find . -type f -print0 | xargs -0 grep -l $@
}

example usage:

[lunix@godzilla ~/tmp]$ ff foo
./slideshow/foo
./slideshow/velocity_puppet_workshop_2009/repo/modules/foo

[lunix@godzilla ~/tmp/test]$ fff  commodo
./file2
[lunix@godzilla ~/tmp/test]$ ll
total 16K
drwxr-xr-x 2 lunix lunix 4.0K 2009-06-29 23:01 .
drwxr-xr-x 4 lunix lunix 4.0K 2009-06-29 22:57 ..
-rw-r--r-- 1 lunix lunix 1.3K 2009-06-29 22:59 file1
-rw-r--r-- 1 lunix lunix 1.7K 2009-06-29 23:00 file2
0

This classical find + exec could also be handy:

find /path/to/ -type f -exec grep -i 'string' {} \;
-1

Try the command:

grep -rn * 

It will work !!

1
  • 2
    Why are you yelling?
    – Telemachus
    Jun 29, 2009 at 11:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .