17

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.

37

You can concatenate several paths for grep to look for:

grep -r "some string" /code/internal/dev/ /code/public/dev/ /code/tools/
  • This is so obvious. Why didn't I think of this?! – David Kennedy Apr 25 '14 at 16:10
  • Actually, this throws an error grep: /code/internal/dev/*.cs: No such file or directory unless I remove the *.cs from the command. This is my fault because I put the *.cs in my question originally. – David Kennedy Apr 25 '14 at 22:57
12

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).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.