这是一个 zsh 扩展的 glob 模式。要点是将目录层次结构下的所有文件与模式匹配,特定子层次结构除外。标题中的模式有效,但我想要一些不需要重复的东西long/path/
。
我尝试了以下方法:
long/path/(^(bad-1|bad-2)/|)**/^*.(complex|pattern)(.)
long/path/(**~(bad-1|bad-2)/*)/^*.(complex|pattern)(.)
添加KSH_GLOB
:
long/path/?(^(bad-1|bad-2)/)**/^*.(complex|pattern)(.)
它们都会导致错误的模式错误。问题似乎出/
在括号中。文档说:
如上所述,用作目录分隔符的“/”可能不会出现在括号内
这让我很困惑,因为(dir/)#
这是一个很好的模式。文档中甚至提到了这一点:
“(foo/)#”形式的路径名组件与由零个或多个与模式 foo 匹配的目录组成的路径相匹配。
我猜这是个例外。
需要明确的是,我正在寻找同等的东西。这不包括:
long/path/^(bad-1|bad-2)/**/^*.(complex|pattern)(.)
因为它排除了诸如long/path/good.pattern
, 和
long/path/(^(bad-1|bad-2)/)#/^*.(complex|pattern)(.)
long/path/**/^*.(complex|pattern)~*/(bad-1|bad-2)/*(.)
因为这些排除了诸如以下内容之类的内容long/path/good/bad-1
。
编辑:顺便提一下,解决方案不一定需要仅使用通配符。如果可以通过添加大括号或历史扩展或其他一些 zsh 工具来完成,那就太好了。
编辑2:按照我自己的想法,我尝试了历史扩展:
long/path/**/^*.(complex|pattern)~!{#$:s/\**//}/(bad-1|bad-2)/*(.)
然而,这失败了,因为 1)当前行的最后一个单词是扩展所在行之前的一个单词,2)因为历史替换似乎是在全局扩展之后完成的(我一定在这里误解了一些东西,因为这似乎违背文档;也许所有扩展都是逐字完成的)。
我还尝试过大括号扩展:
( IFS=\~; printf "%s\n" long/path/{**/^*.(complex|pattern),(bad-1|bad-2)/*(.)} )
它不起作用。 2 个 glob 模式仍然被视为单独的单词,而不是连接成单个模式。我想IFS
这里没有考虑到。说得通。
我还尝试了全局限定符中的替换:
+/**/^*.(complex|pattern)~+/(bad-1|bad-2)/*(.:s,+,long/path,)
但是,当然,这是行不通的,因为替换是在匹配之后而不是之前完成的。
答案1
知道了!双斜杠标记前缀:
long/path//**/^*.(complex|pattern)~*//(bad-1|bad-2)/*(.)
有时,直到您发现某些东西的用途时,您才会意识到它是一种功能。我不敢相信连续斜杠等同于一个的事实实际上可以如此有用。 :)
答案2
(...)
列表/
作为例外的文档:
请注意,分组不能扩展到多个目录:组内有“/”是错误的(这只适用于文件名生成中使用的模式)。有一个例外:作为完整路径段出现的一组 (pat/)# 形式可以匹配一系列目录。
否则可以通过使用表达式来过滤结果来避免重复:
% print -l a/**/*(.)
a/b/c/foo/file
a/b/d/foo/file
a/bad/e/foo/file
a/nope/d/foo/file
% print -l a/**/*(.e:'[[ $REPLY != *(bad|nope)* ]]':)
a/b/c/foo/file
a/b/d/foo/file