18

I am searching for a class declaration on a site with hundreds of PHP files. How can I do this in the current folder and subfolders using grep?

I tested cding to the folder and then something like

grep -r 'class MyClass' *.php
5
  • When this migrates to superuser, you might want to say something about what you wanted that grep -r didn't give you.
    – Jerry Coffin
    Jan 19, 2010 at 16:45
  • I'm confused - didn't you just answer yourself? what were the results of that line? are you trying to further optimize it? please envision reading this question as someone else, because it's confusing and unclear. Jan 19, 2010 at 16:46
  • 1
    @other comments: His grep -r statement isn't giving what he wants, unless all the folders end with .php which is probably not the case... Jan 19, 2010 at 16:47
  • I didn't get anything at all. I assume it didn't work because I know it finds it when im on the folder which has the file with this class
    – Sam3k
    Jan 19, 2010 at 17:48
  • I found the file by luck. When searching for the file in the folder that has the PHP file with that line, it finds it (no need for -r). If I CD to the root and do the search with recursion -r it doesn't return anything.
    – Sam3k
    Jan 19, 2010 at 17:50

11 Answers 11

42

You can try

grep -r --include=*.php "search string" /path/to/dir
2
  • 2
    Works great. I keep forgetting, but fortunately there is always this answer again to help me. :)
    – GolezTrol
    Jan 28, 2014 at 23:24
  • @GolezTrol Glad to know that :-)
    – rahul286
    Apr 2, 2015 at 13:07
8

I'd recommend that you use something like ctags rather than do it with a grep. However, if you want to, you can do something like

 find <top level dir> -name \*.php | xargs grep "class MyClass"

This of course assumes that you have just a single space between class and myClass which might not be true. It's easy to change the regexp to look for any number of whitespaces though.

4

If you use -r, you probably want to recursively search just the current directory:

grep -r 'class MyClass' .

Note the period on the end of the above.

What you told grep was that you wanted it to recursively search every *.php file or directory, but you likely didn't have any directories that ended in a .php extension. The above is the simplest possible way to do the search but also includes files of other types which is especially problematic when you have .svn directories everywhere. If you don't have a lot of other file types, the above is generally sufficient and works well.

Most incarnations of grep don't have a way to specify file extensions that can be searched, so you generally use find in conjunction with it:

find <startDir> -iname '*.php' -exec grep 'class[ \t]*MyClass' {} \;

-iname tells some versions of grep that you want to do a case-insensitive filename search, but many of the non GNU variants of find don't support it, so you could use -name instead.

The -exec option above has the side effect of invoking grep for every file, which is fairly costly.

Still other versions of grep support a + which tells find to append the arguments together to reduce the number of invocations of the executable:

find <startDir> -iname '*.php' -exec grep 'class[ \t]*MyClass' {} + \;

A frequently recommended way to reduce invocations is to use xargs which will fire off grep as few times as possible but as many times as necessary:

find <startDir> -iname \*.php | xargs grep "class[ \t]*MyClass"

xargs is smart enough to pass grep up to the maximum number of arguments supported on the command line, but the above variation doesn't handle certain characters like spaces very well. For example, were 'the file.php' a filename grep would receive 'the' as an argument and 'file.php' as an argument so both wouldn't be found. Instead we use:

find <startDir> -iname \*.php -print0 | xargs -0 grep "class[ \t]*MyClass"

print0 and -0 work together and use arguments followed by a null character so that the argument is completely and unambiguously identified.

f you ever have more than one space after class, or a tab, you might want to allow more characters by changing the regex: class[ \t]*MyClass

This post on StackOverflow has other examples and demonstrates how to exclude certain directories, like .svn directories.

2
  • This searches all files. Also files that are not .php files.
    – GolezTrol
    May 5, 2014 at 18:13
  • I've updated the post to include many other examples. May 6, 2014 at 17:03
3

Using the ever wonderful ack,

ack --php 'class MyClass'
1
grep -r 'class MyClass' *.php

will search through all the folders with names ending with .php

grep -r 'class MyClass' . | grep .php

will search for class MyClass in all files in all sub folders, then only return the ones that have .php in them.

1

In case the matched files might have arbitrary names, you should consider using -print0

find . -type f -name '*.php' -print0 | xargs -0 grep 'class MyClass'

1
grep -rl 'class MyClass' . --include \*.php

l shows only filename

r is recurive

. searches in current folder

--include limits filename extension

1

Probably you want case insensitivity and whitespace tolerance, and grep will terminate if it doesn't find any instances of the desired file pattern in the current directory. It needs to know where to start, as it were, and no files matched produces no starting path.

If there is at least one file of the desired extension, then you can use egrep -ir. find and xargs show their power with single flat (but very big) directories that grep fails on and extra qualifiers (for instance if you want to search for .inc, .php, and php3 files only). But it loses quite a bit of convenience and speed in my experience. For human written class declarations a big problem is going to be whitespace. So use egrep instead of grep. Also LC_ALL=C for extra speed. For convenience, I often use:

LC_ALL=C egrep -irn "class[ ]*myclass([ ]|\n)" * | egrep "\.(inc|php[35]?)"

-i -- case insensitive
-r -- recursive for all file pattern (here *)
-n -- include the line number
LC_ALL=C -- search in ASCII, not in utf8, this is much faster.

[ ]* -- match any number of spaces before the class name
([ ]|\n) -- match a space or newline after the classname

This can still match comments, such as // class myclass exists in file, but I find those to be small relatively, and they can be filtered out with ... | fgrep -v '//'

You can also include more complex file masks (for instance, to catch most .inc and .php files) on the egrep file pattern like so:

egrep "pattern" *.[ip]*

That (with the first options) will be quite fast and mostly limited to php files.

HTH

1
# Search sub-directories, don't follow symlinks.
grep -r -F 'text to search for' *

# Search sub-directories including symlinks.
grep -R -F 'text to search for' *
2
  • This doesn't limit the search to .php files only.
    – gronostaj
    Mar 3, 2021 at 8:09
  • I assumed it was obvious you just add .php for that like: grep -r -F 'text to search for' *.php Mar 3, 2021 at 16:29
0

find . -type f -name *.php -print -exec grep \"class MyClass\" {} \; will give you the contents of the line its found on as well as the path to the file.

0

I know it says using grep, but my personal favourite is to use ack-grep:

ack-grep "class MyClass"

This runs recursively, lists the file name it finds results in, with line numbers for where they are found, and highlights. To specifically target php files you can run:

ack-grep --type-set php:ext:php "path"

The package has a plethora of options; I'd definitely recommend it (and browsing its associated man page) for doing fancy things.

You must log in to answer this question.