我试图编写一个简短的脚本来编写以下位置中找到的所有可执行程序$PATH
:
for dir in $(tr ':' ' ' <<<"${PATH}"); do
for pgm in $dir/*; do
if command -v "${pgm}" >/dev/null 2>&1; then
echo "${pgm}"
fi
done
done | sort >file
在 bash 中,它按预期工作,但一旦内部循环中文件名生成失败,zsh 就会停止处理脚本:
for pgm in $dir/*; do
^^^^^^
...
done
结果,由于 my$PATH
包含一个不包含任何文件 ( /usr/local/sbin
) 的目录,因此在 zsh 中,脚本无法写入随后在目录中找到的可执行文件。
这是显示相同问题的另一个代码:
for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'
在 bash 中,此命令输出:
in the loop
after the loop
并使用代码退出0
。
在 zsh 中,相同的命令输出:
no matches found: /not_a_dir/*
并使用代码退出1
。
shell 之间的行为差异似乎来自选项nomatch
,如下所述man zshoptions
:
不匹配 (+3) <C> <Z>
如果文件名生成模式没有匹配项,则打印错误,而不是将其保留在参数列表中不变。这也适用于初始
~
或=
.
并在man zshexpn
(文件名生成部分)中进行了解释:
该单词将被替换为与模式匹配的排序文件名列表。如果未找到匹配的模式,则 shell 会给出错误消息,除非设置了 NULL_GLOB 选项,在这种情况下会删除该单词;或者除非取消设置 NOMATCH 选项,在这种情况下会保留该单词不变。
因为如果我取消设置nomatch
,zsh的行为就像bash:
unsetopt nomatch
for f in /not_a_dir/*; do
echo 'in the loop'
done
echo 'after the loop'
现在我了解了 bash 和 zsh 之间的行为差异,以及为什么脚本在 zsh 中引发错误,但我想了解为什么文件名生成失败会导致 zsh 立即停止处理脚本。因此,我尝试通过用失败的命令替换失败的文件名生成来重现相同的问题(通过执行not_a_cmd
):
for f in ~/*; do
not_a_cmd
done
echo 'after the loop'
但该脚本的输出在两个 shell 中几乎相同(除了由于 引起的错误消息not_a_cmd
)。特别是,两个 shell 都打印:
after the loop
两个 shell 均以代码退出0
。
为什么失败的文件名生成(如for f in /not_a_dir/*
)会使 zsh 停止处理脚本,而不是失败的命令(如not_a_cmd
)?
我在用着zsh 5.6.2-dev-0 (x86_64-pc-linux-gnu)
。
答案1
这是一个功能,而不是一个错误。