1395

I'm working on a script to grep certain directories:

{ grep -r -i CP_Image ~/path1/;
grep -r -i CP_Image ~/path2/;
grep -r -i CP_Image ~/path3/;
grep -r -i CP_Image ~/path4/;
grep -r -i CP_Image ~/path5/; }
| mailx -s GREP email@domain.example

How can I limit results only to extensions .h and .cpp?

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Jasmine
  • 15,375
  • 10
  • 30
  • 48
  • 14
    Tried `grep -r -i CP_Image ~/path1/*.{h,cpp}`? –  Sep 20 '12 at 16:31
  • 11
    Use [The Silver Searcher](https://github.com/ggreer/the_silver_searcher): `ag -i CP_Image ~/path[1-5] | mailx -s GREP email@domain.com`. Job done. – johnsyweb Oct 09 '13 at 17:25
  • possible duplicate of [grep --exclude/--include syntax (do not grep through certain files)](http://stackoverflow.com/questions/221921/grep-exclude-include-syntax-do-not-grep-through-certain-files) – johnsyweb Oct 09 '13 at 17:48
  • Use egrep (is most likely pre-installed on your system), and then you can use a regex. – Dogweather Oct 09 '13 at 17:53
  • 12
    The GNU guys really messed up when they added `-r` to `grep` to have it search for files as that breaks the UNIX mantra of having tools that "do one thing and do it well". There's a perfectly good tool for finding files with a VERY obvious name. – Ed Morton Oct 09 '13 at 17:58
  • @Dogweather , `egrep` has been deprecated for a long time. From `grep`'s man page: `Direct invocation as either egrep or fgrep is deprecated, but is provided to allow historical applications that rely on them to run unmodified.` To achieve the same result, one must use `grep -E` – mrbolichi Apr 18 '18 at 11:03

12 Answers12

1879

Just use the --include parameter, like this:

grep -inr --include \*.h --include \*.cpp CP_Image ~/path[12345] | mailx -s GREP email@domain.example

That should do what you want.

To take the explanation from HoldOffHunger's answer below:

  • grep: command

  • -r: recursively

  • -i: ignore-case

  • -n: each output line is preceded by its relative line number in the file

  • --include \*.cpp: all *.cpp: C++ files (escape with \ just in case you have a directory with asterisks in the filenames)

  • ./: Start at current directory.

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
Nelson
  • 49,283
  • 8
  • 68
  • 81
441

Some of these answers seemed too syntax-heavy, or they produced issues on my Debian Server. This worked perfectly for me:

grep -r --include=\*.txt 'searchterm' ./

...or case-insensitive version...

grep -r -i --include=\*.txt 'searchterm' ./
  • grep: command

  • -r: recursively

  • -i: ignore-case

  • --include: all *.txt: text files (escape with \ just in case you have a directory with asterisks in the filenames)

  • 'searchterm': What to search

  • ./: Start at current directory.

Source: PHP Revolution: How to Grep files in Linux, but only certain file extensions?

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
  • 9
    You should escape the `*` using `\*.cpp` or `'*.cpp'`. Otherwise it won’t give the expected result when the working directory contains some `*.txt` files. – Melebius Jan 02 '17 at 07:17
  • @Melebius can you explain why it needs escaping - does it have anything to do with the CPP or TXT extensions you mentioned? Or did you just use those as examples? – Simon East Apr 28 '17 at 03:05
  • 2
    @SimonEast These extensions are those used in this question and answer, nothing special otherwise. It would probably work without escaping when using `--include=` but it is important to escape `*` with `--include ` (a space instead of `=`) which feels very similar otherwise. – Melebius Apr 28 '17 at 06:55
  • @Melebius adding to what you wrote, it does work with `--include=`. It also works with `--include`, so long as there are no files matching the pattern in the current directory. I.e., it's safest to escape the pattern when you're not using the `=` syntax, but you can live dangerously if you assume there are no files matching the pattern in the current directory. – TooTone Nov 04 '21 at 22:02
  • Did *not* answer the question! Jasmine asked for *two* file types in a single search! – John Oct 25 '22 at 02:05
91
grep -rnw "some thing to grep" --include=*.{module,inc,php,js,css,html,htm} ./
Community
  • 1
  • 1
Yuri Malov
  • 1,197
  • 8
  • 11
62

Use:

find . -name '*.h' -o -name '*.cpp' -exec grep "CP_Image" {} \; -print
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Amir Afghani
  • 37,814
  • 16
  • 84
  • 124
  • 7
    i'd suggest grouping those `-name` arguments. strange things can happen if you don't. `find . \( -name '*.h' -o -name '*.cpp' \) -exec grep "CP_Image" {} \; -print` – nullrevolution Sep 20 '12 at 21:13
  • 2
    use with additional "-type f" to ignore all directory objects, only interested in files. – kensai Mar 18 '17 at 12:32
  • 2
    I used this method for years and it works but it's a LOT slower than recursive grep since find's exec spawns a separate grep process for each file to be searched. – beaudet Jan 08 '18 at 19:00
  • 1
    Addressing @beaudet's comment, `find` can optionally bundle arguments, reducing invocations of the called process to a minimum. `find . \( -name \*.h -o -name \*.cpp \) -exec grep -H CP_Image {} +` This is suggested but not highlighted in @fedorqui's answer below and is a worthwhile improvement. The `-H` argument to grep here is useful when find only identifies a single matching file. This could eliminate the usage of `-print` in the answer. If your total list of files is sufficiently small, using a recursive shell glob (eg. `{path1,path2}/**/*.{cpp,h}`) might be preferable. – Malcolm Feb 22 '19 at 09:51
36

There isn't any -r option on HP and Sun servers, but this way worked for me on my HP server:

find . -name "*.c" | xargs grep -i "my great text"

-i is for case insensitive search of string.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sam B
  • 27,273
  • 15
  • 84
  • 121
  • 2
    I've come across several servers for web hosting companies that do not have the --include option available for fgrep and this is the command line that I use in those instances. – Borgboy Jan 18 '16 at 21:30
  • The --include option is also not available when using Git for Windows (MinGW/MSys). – Darren Lewis Jan 19 '16 at 14:21
  • @DarrenLewis available in Git Bash for Windows. But strangely, it adds colorful aliases like `ll` but does not add `--color=auto` to grep. – Xeverous Dec 28 '17 at 17:28
  • This should be the accepted answer for completeness, portability, and brevity! – Grant Foster Jan 09 '19 at 20:30
  • 1
    Re *"HP and Sun servers"*: Do you mean for [HP-UX](https://en.wikipedia.org/wiki/HP-UX) and [Solaris](https://en.wikipedia.org/wiki/Solaris_%28operating_system%29)? – Peter Mortensen Apr 24 '21 at 21:13
  • When there is a space character in the file name, it would be treated as two file names. Any solution to solve this problem? – hyz Dec 24 '21 at 07:54
  • @hyz Use `find ... -exec grep "string" {} +` (or `\;` instead of `+` at the end if your `find` is too old to support `+`). See also https://mywiki.wooledge.org/BashFAQ/020 – tripleee May 15 '22 at 05:27
21

This answer is good:

grep -r -i --include \*.h --include \*.cpp CP_Image ~/path[12345] | mailx -s GREP email@domain.example

But it can be updated to:

grep -r -i --include \*.{h,cpp} CP_Image ~/path[12345] | mailx -s GREP email@domain.example

Which can be simpler.

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
hao
  • 639
  • 5
  • 11
  • 1
    What does *"The below answer"* refer to? References to relative positions of answers are not reliable as they depend on the view (votes/oldest/active) and changing of the accepted answer and change over time (for votes, active, and accepted state). Please respond by [editing your answer](https://stackoverflow.com/a/48533603/63550), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Apr 24 '21 at 21:21
  • Can we simply use grep command along with ls command? – Harsha Oct 27 '21 at 15:30
  • @Harsha Sure. use pipe to make the output of ls as the input of grep. such as ls | grep *.h – hao Dec 25 '21 at 12:26
  • Could you provide me a link that would give explanations as to how I can fully understand this? – Harsha Dec 26 '21 at 10:43
  • @Harsha https://docs.oracle.com/cd/E19504-01/802-5826/6i9iclf5k/index.html – hao Dec 27 '21 at 03:37
  • note: you can combine single-letter options. `grep -r -i` is the same as `grep -ri`. I mention this because I often like to use other options like '-n' and '-H' so this becomes `grep -Hinr --include ...` – gMale Jun 15 '22 at 19:01
14

Since this is a matter of finding files, let's use find!

Using GNU find you can use the -regex option to find those files in the tree of directories whose extension is either .h or .cpp:

find -type f -regex ".*\.\(h\|cpp\)"
#            ^^^^^^^^^^^^^^^^^^^^^^^

Then, it is just a matter of executing grep on each of its results:

find -type f -regex ".*\.\(h\|cpp\)" -exec grep "your pattern" {} +

If you don't have this distribution of find you have to use an approach like Amir Afghani's, using -o to concatenate options (the name is either ending with .h or with .cpp):

find -type f \( -name '*.h' -o -name '*.cpp' \) -exec grep "your pattern" {} +
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

And if you really want to use grep, follow the syntax indicated to --include:

grep "your pattern" -r --include=*.{cpp,h}
#                      ^^^^^^^^^^^^^^^^^^^
Community
  • 1
  • 1
fedorqui
  • 275,237
  • 103
  • 548
  • 598
  • 1
    I needed a script which returned exit status 1 if the grep command matched any files. I started out using find+xargs+grep. But the fact that xargs returns exit status 123 if grep returns 1 made things more complicated. It was more straightforward to just use grep in my case. – Guildenstern Mar 10 '22 at 09:16
11

The easiest way is:

find . -type  f -name '*.extension' 2>/dev/null | xargs grep -i string

Add 2>/dev/null to kill the error output.

To include more file extensions and grep for password throughout the system:

find / -type  f \( -name '*.conf' -o -name "*.log" -o -name "*.bak" \) 2>/dev/null |
xargs grep -i password
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user3640130
  • 443
  • 5
  • 5
5

ag (the silver searcher) has pretty simple syntax for this

       -G --file-search-regex PATTERN
          Only search files whose names match PATTERN.

so

ag -G *.h -G *.cpp CP_Image <path>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eldamir
  • 9,888
  • 6
  • 52
  • 73
2

You should write "-exec grep " for each "-o -name ":

find . -name '*.h' -exec grep -Hn "CP_Image" {} \; -o -name '*.cpp' -exec grep -Hn "CP_Image" {} \;

Or group them by ( )

find . \( -name '*.h' -o -name '*.cpp' \) -exec grep -Hn "CP_Image" {} \;

Option '-Hn' shows the file name and line.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
where23
  • 483
  • 3
  • 9
1

Here is a method I normally use to find .c and .h files:

tree -if | grep \\.[ch]\\b | xargs -n 1 grep -H "#include"

Or if you need the line number as well:

tree -if | grep \\.[ch]\\b | xargs -n 1 grep -nH "#include"
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tom
  • 414
  • 4
  • 17
0

If you want to filter out extensions from the output of another command e.g. "git":

files=$(git diff --name-only --diff-filter=d origin/master... | grep -E '\.cpp$|\.h$')

for file in $files; do
    echo "$file"
done
nvd
  • 2,995
  • 28
  • 16