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?

Jay
  • 2,270

4 Answers4

29

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

grep -lr --include='*.c' search-pattern .
enzotib
  • 93,831
3

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.)
gertvdijk
  • 67,947
  • 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 '13 at 14:12
  • @enzotib Thanks, fixed it using -print0 option and xargs --null. – gertvdijk Jul 14 '13 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
  • 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. – Craig Hicks Oct 18 '20 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 '20 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"
Jebiel
  • 9