Search code examples
bashshellgrepbrace-expansion

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.


Solution

  • --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