Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Glob and grep



On 12/17/19, Nick Cross <zsh@xxxxxxxxx> wrote:
> On 16/12/2019 23:10, Mikael Magnusson wrote:
>> On 12/16/19, Nick Cross <zsh@xxxxxxxxx> wrote:
>>>
>>> Hi,
>>>
>>> I would like to condense the following into a single grep / glob
>>> expression where I search recursively through a directory tree, ignoring
>>> any sub-trees starting with 'test' or 'target'.
>>>
>>> grep <pattern> **/*.groovy  | egrep -v "(/test/|/target/)"
>>>
>>> Am I right in thinking I can add (.) to e.g. *.groovy to ensure I only
>>> search for files as well ?
>>
>> I think what you want is
>> grep <pattern> (^(test|target)/)#*.groovy(.)
>>
>
> Thats great thanks! Works perfectly.
>
> So to break it down - the ^ negates the or'd block. Can you explain how
> the wrapped (../) and # then works please?

Sure, you already know about the **/ operator, what you probably
didn't know is that it's a shorthand for (*/)# which means */ repeated
0 or more times. We can replace the * by another pattern here, for
example ((abc|xyz)/)# would match abc/abc and abc/xyz/xyz/abc/abc etc.

A good way to think about (^foo) is like a * that will not expand to
exactly foo. If you write *^hello you might think you'll match every
file not ending in hello, but in fact you will match every file; the
first * will simply match the entire string and then ^hello will match
the empty string (or indeed "ello" or some other valid combination).

The above method should be the most efficient way to do this, using
**/*.groovy~*/(test|target)/* will first recurse through the entire
tree, including test/ and target/, generate the whole list of matches,
and then filter it by the pattern(s) given by ~. Depending on how
large the test/target directories are, this can be a significant
difference. It is probably easier to think about how ~ works though.

-- 
Mikael Magnusson



Messages sorted by: Reverse Date, Date, Thread, Author