在 zsh 中使用花括号扩展匹配文件

Pet*_*nak 5 zsh wildcards brace-expansion

bash我可以使用花括号来模式匹配多个文件,例如

ls *.{dot,svg,err}
Run Code Online (Sandbox Code Playgroud)

如果没有特定扩展名的文件,我会收到警告,但会列出其余文件。

ls: 无法访问 '*.err': 没有那个文件或目录

但是,在zsh我得到一个错误并且没有为现有文件产生输出

zsh:未找到匹配项:*.err

这也适用于删除,在尝试删除文件时:

rm -f *.{dot,svg,err}
Run Code Online (Sandbox Code Playgroud)

将在bash没有任何错误或警告的情况下删除所有匹配的文件,但zsh它会出错并且根本不会删除任何内容。

有没有办法使zsh行为与 相同bash,或者是否有另一种方法可以轻松删除/列出现有文件¹?


¹显然我可以做类似的事情,find . -name '*.dot' -or -name '*.svg' -or -name '.err' | xargs rm但这并不是特别容易/实用。

Sté*_*las 13

你没有得到的警告bash,你会得到一个错误ls(你会发现,ls退出状态非0,表示它已失败)。

zshand 中bash, ,{...}都不是通配符,而是在通配符之前发生的扩展。

在:

ls -d -- *.{dot,svg,err}
Run Code Online (Sandbox Code Playgroud)

(你忘记了-d--btw),shell 扩展了{...}第一个:

ls -d -- *.dot *.svg *.err
Run Code Online (Sandbox Code Playgroud)

然后做 glob。bash像大多数 Bourne-like shell 一样,不匹配的 glob 按原样传递。在 on 时zsh,不匹配的 glob 是一个错误。

了解如何rm -f [ab].cbash可以删除该[ab].c文件,如果没有所谓的文件a.c也没有b.c。在 中zsh,你会得到一个不匹配的错误。请参阅 中的failglob选项bash以获得类似的行为。

ls -d -- *.{dot,svg,err}(N)
Run Code Online (Sandbox Code Playgroud)

inzshnullglob在所有 3 个 glob 上启用该选项,以便如果 glob 不匹配,它们将被删除,但这可能不是您想要的,因为如果没有一个 glob 与任何文件匹配,则命令将变为:

ls -d --
Run Code Online (Sandbox Code Playgroud)

哪个会列出 .(当前目录)。

最好的方法是使用一个匹配具有 3 个扩展名之一的文件的 glob:

ls -d -- *.(dot|svg|err)
Run Code Online (Sandbox Code Playgroud)

这将给出一个排序的文件列表,除非没有找到与该模式匹配的文件lsls否则将运行。

您还可以选择使用或启用sh/ bash(伪造的 IMO)行为。稍微好一点的方法是启用该行为(这也是 Bourne shell 发布之前 Unix shell 的行为):emulate shunsetopt nomatchcsh

setopt cshnullglob
Run Code Online (Sandbox Code Playgroud)

使用该选项,仅当命令行上的所有glob无法匹配时,才会取消该命令。如果至少有一个匹配,则删除所有不匹配的,因此:

ls -d -- *.{dot,svg,err}
Run Code Online (Sandbox Code Playgroud)

将扩展dot,svgerr依次省略缺失的。

如果您想比较不同方法的参数顺序的影响,您需要一个命令(与 相反ls)在显示之前不对它们进行排序。使用 GNU ls,您可以-U为此传递选项,或者因为ls只在此处打印其参数,只需使用即可printf '%s\n' *...