It's an zsh extended glob pattern. The point is to match all files with a pattern under a directory hierarchy with the exception of specific sub-hierarchies. The pattern in the title works, but I wanted something that doesn't require repeating long/path/
.
I tried the following:
long/path/(^(bad-1|bad-2)/|)**/^*.(complex|pattern)(.)
long/path/(**~(bad-1|bad-2)/*)/^*.(complex|pattern)(.)
with KSH_GLOB
added:
long/path/?(^(bad-1|bad-2)/)**/^*.(complex|pattern)(.)
They all result in a bad pattern error. It seems the problem is having a /
in parentheses. The documentation says:
As mentioned above, a `/' used as a directory separator may not appear inside parentheses
That confused me because (dir/)#
is a good pattern. It's even mentioned in the docs:
A pathname component of the form `(foo/)#' matches a path consisting of zero or more directories matching the pattern foo.
I guess it's the exception.
To be clear, I'm looking for equivalents. That excludes:
long/path/^(bad-1|bad-2)/**/^*.(complex|pattern)(.)
since it excludes things like long/path/good.pattern
, and
long/path/(^(bad-1|bad-2)/)#/^*.(complex|pattern)(.)
long/path/**/^*.(complex|pattern)~*/(bad-1|bad-2)/*(.)
since those exclude things like what's under long/path/good/bad-1
.
EDIT: Just to mention, a solution doesn't necessarily need to use only globbing. If it can be done by adding use of brace or history expansion or some other zsh facility, that's great.
EDIT 2: Following my own ideas, I tried history expansion:
long/path/**/^*.(complex|pattern)~!{#$:s/\**//}/(bad-1|bad-2)/*(.)
However, this failed because 1) the last word of the current line is the one prior to the one that the expansion is in, and 2) because history substitution seems to be done after glob expansion (I must be misunderstanding something here because that seems to go against the docs; maybe all expansions are done on a word by word basis).
I also tried brace expansion:
( IFS=\~; printf "%s\n" long/path/{**/^*.(complex|pattern),(bad-1|bad-2)/*(.)} )
It didn't work. The 2 glob patterns were still taken as separate words instead of being joined into a single pattern. I guess IFS
isn't taken into consideration here. Makes sense.
I also tried substitution in glob qualifiers:
+/**/^*.(complex|pattern)~+/(bad-1|bad-2)/*(.:s,+,long/path,)
But, of course, that wouldn't work because substitution would be done after matching not before.