1

I wish to grep certain files that only exist below a specific subdirectory. Here, I only want .xml files if they exist below the /bar/ subdirectory:

./file0.xml
./.git/file1a.xml
./.git/bar/file1b.xml
./.svn/file2a.xml
./.svn/foo/bar/baz/file2b.xml
./path1/file3.xml
./path1/foo/file4.xml
./path2/foo/bar/file5.xml
./path2/foo/baz/file6.xml
./path3/bar/file7.xml
./path3/foo/bar/baz/file8.xml

I want only the following files to be grepped: file5.xml, file7.xml, file8.xml

To exclude .git and .svn, I came up with:

grep -r --exclude-dir={.git,.svn} --include=\*.xml "pattern"

which still searches file3.xml through file8.xml.

If I grep -v the undesired directories:

grep -r --exclude-dir={.git,.svn} --include=\*.xml "pattern" | grep -v /bar/

I get the desired results, but it spends a lot of time parsing the non-/bar/ files.

Using find to find the xml files under /res/, I get the desired results (and it's much faster than the above result):

find . -type d \( -name .git -o -name .svn \) -prune -o \
       -path '*/bar/*.xml' -exec grep "pattern" {} +

I'm trying to avoid this, however, as I use this within a script and don't want to be limited to starting the search in the top ./ directory.

Is there a way to accomplish this using only grep (so it doesn't prevent the user from specifying additional grep options and/or starting search directories)? Perhaps something like:

grep -r --exclude-dir={.git,.svn} --include=\*/bar/\*.xml "pattern"
OnlineCop
  • 4,019
  • 23
  • 35

1 Answers1

1

find+grep is definitely a good approach. You could make it more flexible by defining a function that inserts arguments in strategic places. For example:

search() {
    local dir=$1
    local pattern=$2
    local args=("${@:3}")

    find "$dir" -type d \( -name .git -o -name .svn \) -prune -o \
        -path '*/bar/*.xml' -exec grep "${args[@]}" "$pattern" {} +
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578