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