46

It may sound like I'm asking the same thing as this question, but I have different requirements. This is an example of my filesystem:

  • /code/
    • internal/
      • dev/
      • main/
    • public/
      • dev/
      • main/
      • release/
    • tools/

/code/internal/dev/, /code/public/dev/ and /code/tools/ contain subdirectories for multiple projects. I work almost exclusively in the dev branches of /code/internal/ and /code/public/, and often I want to search for a text string in those directories along with /code/tools/ (which has no branches). In these instances I have to do three separate commands:

$ grep -r "some string" /code/internal/dev/
$ grep -r "some string" /code/public/dev/
$ grep -r "some string" /code/tools/

I'd like to know if there's a single command to do this. If not, I would most likely need to write a simple bash script.

2 Answers2

69

You can concatenate several paths for grep to look for:

grep -r "some string" /code/internal/dev/ /code/public/dev/ /code/tools/
vestlen
  • 204
Artur Szymczak
  • 1,933
  • 12
  • 12
22

If you want to make maximal use of wildcards (and the hierarchy you posted is complete), you can do

grep -r "some string" /code/{*/dev,tools}/*.cs

Explanation:

The first step done is expansion of the braced list. foo{bar,baz}qux expands to foobarqux foobazqux. That is, there's a separate word generated for each comma-separated item in the list, with the prefix and postfix part attached to each. You can see how that works by doing

echo A{1,2,3,4}B

which outputs

A1B A2B A3B A4B

Note that this also works with multiple braces, and also with empty arguments; for example

echo {,1,2}{0,1,2}:{2,3}

gives

0:2 0:3 1:2 1:3 2:2 2:3 10:2 10:3 11:2 11:3 12:2 12:3 20:2 20:3 21:2 21:3 22:2 22:3

So after brace expansion, your command looks like this:

grep -r "some string" /code/*/dev/*.cs /code/tools/*.cs

The next step is wildcard expansion. You already know that for the *.cs part, but it also works for intermediate directories; moreover, if a / follows, only directories are matched. Therefore given your hierarchy (and making up file names for the .cs files), you'll get the command:

grep -r "some string" /code/internal/dev/file1.cs /code/internal/dev/file2.cs /code/public/dev/file3.cs /code/tools/file4.cs /code/tools/file5.cs

Only after all this has happened, grep is called with this list of arguments (note that the same happens with your original commands; grep never gets to see the *; expanding that is completely done by bash before calling grep).

celtschk
  • 10,844