使用 glob 限定符搜索时如何排除一个或多个文件名后缀?
工作正常,这里我们匹配文件名后缀,而不是排除它们:
print -rC1 **/*.(txt)(.)
print -rC1 **/*.(jpg|png)(.)
我们并不是试图排除它们,但行不通:
print -rC1 **/*.(^txt)(.)
print -rC1 **/*.(^jpg|^png)(.)
zsh 5.9 (x86_64-apple-darwin23.0)
使用 glob 限定符搜索时如何排除一个或多个文件名后缀?
工作正常,这里我们匹配文件名后缀,而不是排除它们:
print -rC1 **/*.(txt)(.)
print -rC1 **/*.(jpg|png)(.)
我们并不是试图排除它们,但行不通:
print -rC1 **/*.(^txt)(.)
print -rC1 **/*.(^jpg|^png)(.)
zsh 5.9 (x86_64-apple-darwin23.0)
使用该
^x
模式需要在 shellEXTENDED_GLOB
中设置 shell 选项zsh
:之后,您应该能够查找文件名后缀既不
.foo
也不.bar
使用该模式的任何常规非隐藏文件请注意,
*.(^foo|^bar)
这与^*.(foo|bar)
. 前者的意思是“任何包含 a 的文件名,.
后面跟着不是foo
或不是的东西bar
”(基本上所有至少有一个点的文件,因为没有任何东西可以同时是两者foo
)bar
,而后者的意思是“没有文件名”以.foo
or.bar
结尾”。如果您不包含
./
前缀,请注意,不要忘记分隔的--
或选项,否则会成为任意命令执行漏洞。-
print
所以在你的情况下:
全局限定符实际上并不是这里的问题。事实上,他们使一些示例发挥了作用。这里存在三个独立的问题:如何解析 glob 限定符、glob 运算符的语法以及控制它们的选项。
圆括号总是被接受用于在 glob 中进行分组,尽管在某些情况下,它们在到达作为 glob 一部分的阶段之前会被解析为其他内容(算术表达式、我稍后将扩展的 glob 限定符、函数定义等)。在 中
*.(txt)(.)
,最后一个带括号的块(.)
被解析为glob 限定符,并且(txt)
是仅匹配确切文本的模式txt
。该模式
*.(jpg|png)(.)
使用glob 运算符|
,意思是“或”。该部分与和(jpg|png)
均匹配。jpg
png
默认情况下,
^txt
仅匹配其自身。仅当设置该选项时,才会识别glob运算符。 此选项始终在补全函数和 中设置,但默认情况下处于关闭状态。它默认关闭的唯一原因是为了严格的向后兼容性(它最初并不存在,但已经存在了 30 多年)。很少有实际情况会产生影响(主要是如果您想在命令参数中传递,或而不引用它),即使确实如此,反斜杠或任何其他引号也可以修复该问题。我建议将您的. 启用此选项后,匹配除 之外的任何内容。^
extended_glob
zmv
#
^
~
setopt extended_glob
.zshrc
^txt
txt
该模式
(^jpg|^png)
匹配任何不是jpg
或不是 的内容png
。任何字符串都不是 jpg 或非 png,因此(^jpg|^png)
匹配所有内容。要匹配既不是jpg
也不是的任何内容png
,请使用^(jpg|png)
. 如果您想要否定的部分后面还有更多内容,则需要后面的两个括号,例如*.(^(jpg|png)).gz
匹配foo.bar.gz
andfoo.txt.gz
但不匹配foo.jpg.gz
orfoo.png.gz
。现在让我们回到如何解析glob 限定符。默认情况下,末尾带括号的组被解析为全局限定符,除非它包含
|
或嵌套括号或~
(extended_glob
启用时)。在
*.(txt)
,中(txt)
被解析为 glob 限定符(因此*.(txt)
匹配名称以点结尾且可执行且设置了粘性位的文件)。使用双括号可以避免 glob 限定符解析。*.(png|jpg)
不会被解释为使用 glob 限定符,因为|
.*.(txt)(.)
已经被(.)
解释为 glob 限定符,因此该(txt)
部分不能是 glob 限定符。您可以通过取消设置来禁用 glob 限定符解析
bare_glob_qual
。我不推荐它,因为模棱两可的模式很少见,而且当您确实需要全局限定符时,它需要额外的输入。注意