我刚刚读完一篇题为“为什么你不应该解析 ls 的输出”的文章。我想循环一个目录中的每个文件,文章指出以下是最基本和最理想的方法之一。
for f in *; do
echo $f
# …do stuff
done
Run Code Online (Sandbox Code Playgroud)
我不明白的一件事是如何忽略某些文件和目录?我使用 ZSH 并已打开extendedglob
。因此,我认为这将是可行的:for f in *^.jpg; do…
,但它只回显“*^.jpg”,没有文件。
我怎样才能保持循环大部分不变,但结合排除某些模式的能力?额外的问题:你能让 glob 递归吗?
Zsh 提供了非常好的枚举文件的方法。它们记录在“文件名生成”下的手册中,但 zsh 手册不是很容易遵循。
默认情况下,如果没有匹配的文件,您将收到错误消息,这在命令行中通常是可取的,但在脚本中则不然。要禁用此错误,请将其放在脚本开头附近以打开null_glob
选项:
setopt null_glob
…
for x in *; do …
Run Code Online (Sandbox Code Playgroud)
或使用N
glob 限定符:
# Reliably iterate over non-dot files
for x in *(N); do …
Run Code Online (Sandbox Code Playgroud)
上面的任何一个片段都会遍历当前目录中所有不是点文件的文件。如果没有这样的文件,循环根本不会运行(不像 sh 循环体在未扩展的 glob 上无益地运行一次*
,而默认 zsh 则 glob 导致错误)。
要在迭代中包含点文件,请打开该got_glob
选项或使用D
glob 限定符。
# Reliably iterate over all files in the current directory
for x in *(DN); do …
Run Code Online (Sandbox Code Playgroud)
我推荐使用 glob 限定符方法,即使它更冗长,因为即使您将该片段复制粘贴到未打开相同选项的脚本中,它也会保持相同的行为。
要根据名称排除某些文件,您可以使用glob 运算符 ^
和~
. 请注意,这些需要setopt extended_glob
在您的脚本² 中(这不是从您的运行时环境或您的.zshrc
¹继承而来)。例如,要排除*.jpg
文件,写入*~*.jpg
或^*.jpg
.
(请注意,*^.jpg
默认情况下,您编写的内容会导致 zsh 出现错误。如果您看到*^.jpg
打印出来,那么您要么在nomatch
关闭的情况下运行 zsh (默认情况下它处于打开状态),这通常仅在模拟 sh 时完成或 ksh,否则您实际上是在 sh shell 下运行该脚本,可能是因为它在顶部缺少一个shebang 行。)
setopt extended_glob
# Iterate over all the files in the current directory except *.jpg and .*
for x in *~*.jpg(N); do …
Run Code Online (Sandbox Code Playgroud)
如果您想递归遍历子目录,这很简单:只需使用**/
.
setopt extended_glob
# Iterate over all the files in the current directory and its (grand-)*children except *.jpg and .*
for x in **/*~*.jpg(N); do …
Run Code Online (Sandbox Code Playgroud)
通过glob qualifiers,您还可以根据名称以外的条件选择文件。例如,仅循环常规文件,不包括目录、符号链接等:
# Iterate over all regular files in the current directory and its (grand-)*children except *.jpg and .*
for x in **/*(.DN); do …
Run Code Online (Sandbox Code Playgroud)
您甚至可以运行任意代码来决定是否使用e
or +
glob 限定符包含文件。但是,当在 for 循环中使用通配符模式时,将过滤代码放在循环体的顶部会更清晰。
¹你可以把setopt extended_glob
在~/.zshenv
,但我强烈建议不要使用.zshenv
,因为这使得关于什么是在那里将打破在不同的机器或不同的帐户上假设的任何脚本。
²如果您正在编写 zsh 完成功能,则这些功能会在extended_glob
打开的环境中运行。