18

I tried to recursively search a pattern in all the .c files in the following way

> grep -lr search-pattern *.c

But got this as the output

> grep: *.c: No such file or directory

When I use this:

> grep -lr search-pattern *

I get plenty of . c files in the directories with the pattern.

What is wrong with the earlier expression?

0

4 Answers 4

29
+50

I suggest to use the --include option of grep:

grep -lr --include='*.c' search-pattern .
4
  • 1
    +1. I didn't know the --include=GLOB option. In combination with the recursive option this is very powerful and doesn't require find. Nice!
    – gertvdijk
    Jul 14, 2013 at 14:13
  • Late to the party here, but I'm confused about what the . at the end of the command is for. Feb 5, 2014 at 21:28
  • 1
    @NathanJones: grep requires one or more filenames or directories in which to search. The dot says: search in the current directory
    – enzotib
    Feb 6, 2014 at 8:00
  • This answer is amazing. thank you! Just what I needed rn actually! Oct 18, 2020 at 9:51
3
+100

The *.c pattern is evaluated by your shell. It applies to the current directory, just like you would using ls *.c.

I think what you want instead is to find all files matching the *.c pattern (recursively) and have grep search for you in it. Here's a way to do that:

find . -name "*.c" -print0 | xargs --null grep -l search-pattern

It uses xargs to append the search results by find.


Alternatively, use the -exec option to find, e.g.:

find . -name "*.c" -exec grep -l search-pattern "{}" \;

Also, I'm not sure if you really want the -l option to grep. It will stop at the first match:

-l, --files-with-matches
      Suppress normal output; instead print the name of  each
      input  file  from which output would normally have been
      printed.  The scanning will stop on  the  first  match.
      (-l is specified by POSIX.)
2
  • The find/xargs syntax breaks on filenames containing spaces. The -L option of grep stop on first match of each file and continue with next file: if one only want to see if the pattern is contained at least once in each given file, it is quicker.
    – enzotib
    Jul 14, 2013 at 14:12
  • @enzotib Thanks, fixed it using -print0 option and xargs --null.
    – gertvdijk
    Jul 14, 2013 at 14:17
0

As the grep documentation states in its discussion of the -r option:

"If no folder name is given, grep command will search the string inside the current working directory."

therefore, your original command line (ignoring the -l flag):

grep -lr search-pattern *.c

is recursively searching, within the files in the current directory whose filenames end in .c, for "search-pattern". It will find nothing if there are no such files in the current directory.

Your second command line (again ignoring the -l flag):

 > grep -lr search-pattern *

is recursively searching for "search-pattern" in all files in the current directory and all subdirectories (because * is being interpreted as a wild card for "folder name").

Your two command lines will both "recursively search a pattern in all the .c files", the first in the current directory, the second by recursive descent of the current directory and all subdirectories, if you eliminate the "-l" flag from both of them.

The discussion that follows presumes that your intent is simply recursing over the .c files in the current directory for your search (as implied by your first command line).

Discussion:

My preference would be to leave the task of finding and listing the files of interest to ls the utility that is built for that purpose, having it list them one filename per line. I would then construct the command line for grep with xargs (the utility built for that purpose) from the output of ls and the pattern that you want grep to look for (thus using grep in the most natural way). The resulting command looks like this:

ls -1 *.c | xargs grep "C.*t"

Test Case:

In a directory with the following contents:

Erlang hello.cs hello.exe somefile.c someotherfile.c

to follow your description of the intent, I will expect my command line to find the files somefile.c and someotherfile.c, and search within these for a pattern that starts with a capital C and ends with a lowercase t.

The file somefile.c consists of:

Ignore this!
Content
... this too ...
... and this.

The file someotherfile.c consists of

Content to find
Ingore this

and our command

ls -1 *.c | xargs grep "C.*t"

produces this result:

somefile.c:Content
someotherfile.c:Content to find

supplying the -v argument to grep gives us the inverse result, i.e., the command:

ls -1 *.c | xargs grep -v "C.*t"

gives the result:

somefile.c:Ignore this!
somefile.c:... this too ...
somefile.c:... and this.
someotherfile.c:Ingore this
2
  • The OP actually wants to search recursively through sub directories for *.c files, so ls -1 *.c does not answer the question. Perhaps find . -name "*.c" | xargs ... would be better? I like your use of xargs, although it is not as direct as the selected answer using the original grep. However, with your obvious misinterpretation of the question, and on top of that wandering off topic with the -v option, you are actually in danger of getting downvotes. Worth fixing. Oct 18, 2020 at 20:18
  • 1
    Technically if you read the reason why it has a bounty, they are just doing it to get more upvotes for the existing answer.
    – rtaft
    Oct 19, 2020 at 13:05
-1

I know this is a rather old thread, but since I had the same question, I wanna share my preferred way of acheiving the same, In a much shorter form.

ls | grep "file.*.c"
1
  • 2
    The question is about searching for a pattern not about searching a file.
    – Jay
    Sep 19, 2019 at 15:39

You must log in to answer this question.

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