Geoffrey Anderson
Geoffrey Anderson

Reputation: 1574

Weird issue when running grep with the --include option

Here is the code at the bash shell. How is the file mask supposed to be specified, if not this way? I expected both commands to find the search expression, but it's not happening. In this example, I know in advance that I prefer to restrict the search to python source code files only, because unqualified searches are silly time wasters.

So, this works as expected:

grep -rni '/home/ga/projects' -e 'def Pr(x,u,v)'

/home/ga/projects/anom/anom.py:27:def Pr(x,u,v): blah, blah, ...

but this won't work:

grep --include=\*.{py} -rni '/home/ga/projects' -e 'def Pr(x,u,v)'

I'm using GNU grep version 2.16.

Upvotes: 3

Views: 310

Answers (3)

Adrien H
Adrien H

Reputation: 793

Your command fails because of the braces '{}'. It will search for it in the file name. You can create a file such as 'myscript.{py}' to convince yourself. You'll see it will appear in the results.

The correct option parameter would be '*.py' or the equivalent \*.py. Either way will protect it from being (mis)interpreted by the shell.

On the other side, I can only advise to use the command find for such jobs :

find /home/ga/projects -regex '.*\.py$' -exec grep -e "def Pr(x,u,v)" {} +

That will protect you from hard to understand shell behaviour.

Upvotes: 2

Paulo Mattos
Paulo Mattos

Reputation: 19339

Try like this (using quotes to be safe; also better readability than backslash escaping IMHO):

grep --include='*.py' ...

your \*.{py} brace expansion usage isn't supported at all by grep. Please see the comments below for the full investigation regarding this. For the record, blame this answer for the resulting brace wars ;)

By the way, the brace expansion works generally fine in Bash. See mklement0 answer for more details.


Ack. As an alternative, you might consider switching to ack instead from now on. It's a tool just like grep, but fully optimized for programmers.

It's a great fit for what you are doing. A nice quote about it:

Every once in a while something comes along that improves an idea so much, you can't ignore it. Such a thing is ack, the grep replacement.

Upvotes: 1

mklement0
mklement0

Reputation: 439257

--include=\*.{py} looks like a broken attempt to use brace expansion (an unquoted {...} expression).

However, for brace expansion to occur in bash (and ksh and zsh), you must either have:

  • a list of at least 2 items, separated with ,; e.g. {py,txt}, which expands to 2 arguments, py and txt.

  • or, a range of items formed from two end points, separated with ..; e.g., {1..3}, which expands to 3 arguments, 1, 2, and 3.

Thus, with a single item, simply do not use brace expansion:

--include=\*.py

If you did have multiple extensions to consider, e.g., *.py as well as *.pyc files, here's a robust form that illustrates the underlying shell features:

'--include=*.'{py,pyc}

Here:

  • Brace expansion is applied, because {...} contains a 2-item list.
  • Since the {...} directly follows the literal (single-quoted) string --include=*., the results of the brace expansion include the literal part.
  • Therefore, 2 arguments are ultimately passed to grep, with the following literal content:
    • --include=*.py
    • --include=*.pyc

Upvotes: 3

Related Questions