要让 zsh 管道报告来自任意阶段的失败,请在运行它之前启用 PIPE_FAIL 选项。在 Mac 上的终端中,输入 setopt PIPE_FAIL;在脚本中,将该行放在管道之前。管道随后会返回其最右侧失败命令的退出状态,若所有命令均成功则返回零。如果后续命令不能覆盖该结果,请在管道执行后立即捕获 $?。zsh 选项手册记录了此行为。
为什么失败的管道看起来可能像成功了
默认情况下,zsh 会将管道中最后一个命令的退出状态赋予整个管道。因此,即使先前的命令失败,只要最后一个命令成功,整个管道看起来也会是成功的。例如,即使 grep 没有找到匹配的行,下面的最后一个 cat 也能成功:
printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- $?
zsh 手册将管道定义为由 | 连接的命令,并指出其值通常是最后一个命令的值。状态为零表示成功;非零状态表示命令或管道报告了失败。zsh 管道文档描述了此默认规则。
为管道启用 PIPE_FAIL
在终端会话中,于运行命令之前设置该选项:
setopt PIPE_FAIL
printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- $?
由于 grep 失败,即使 cat 成功,最终状态现在也是非零的。PIPE_FAIL 会采用管道中最右侧的非零状态。这样既能保留有意义的失败信息,又能返回由 zsh 选取的实际状态码。要在同一会话中再次关闭该行为,请运行 unsetopt PIPE_FAIL。
对于脚本,请将该选项置于顶部附近,即解释器行之后且在任何受影响的管道之前:
#!/bin/zsh
setopt PIPE_FAIL
printf '%s\n' 'ready' | grep 'missing' | cat
pipeline_status=$?
if (( pipeline_status != 0 )); then
print -u2 -- "Pipeline failed with status $pipeline_status"
exit "$pipeline_status"
fi
显式的状态赋值至关重要:$? 表示上一个命令的状态,因此后续的 print、与变量相关的命令或条件判断可能会更改它报告的内容。立即保存该值后,脚本便可以在执行其他工作后再报告它或携带它退出。此示例会向启动脚本的程序返回失败状态;它不会在早期阶段失败时立即阻止后续管道各个命令的运行。
需要各阶段的结果时使用 pipestatus
PIPE_FAIL 用于总结整个管道。如果你还需要确认具体是哪个命令返回了哪个代码,zsh 提供了特殊数组 pipestatus,其中包含上一个管道中每个命令的状态。请立即读取它,因为另一个管道会覆盖这些值。zsh 参数参考记录了该数组。
printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- "Stage statuses: ${pipestatus[*]}"
对于这个包含三个命令的示例,输出显示 printf 为零,grep 为非零值,cat 为零。具体的非零代码取决于命令本身。如果你既需要总体结果也需要每个阶段的结果,请使用 PIPE_FAIL 并立即捕获总体的 $?;在诊断各阶段时,在单独的管道运行后立即检查 pipestatus,或者设计脚本先记录该数组,然后再根据保存的数据计算所需的聚合结果。请避免在管道与你打算读取的状态之间插入其他命令或管道。
确保设置在正确的 Shell 中生效
要将该选项应用于交互式 zsh 会话中的后续命令,请在该会话中运行 setopt PIPE_FAIL。若要为每个新 zsh 会话启用它,请将该行添加到 ~/.zshrc 中,然后打开一个新的终端窗口或使用 source ~/.zshrc 读取该文件。Apple 的终端指南指出 zsh 是默认的 shell,同时也指出设置或管理员可以选择不同的 shell。在依赖特定于 zsh 的语法之前,请确认当前处于活动状态的 shell。Apple 终端指南解释了终端如何选择 shell。
以 #!/bin/zsh 开头的脚本会直接请求 zsh,无论终端配置了何种交互式 shell。如果脚本改由其他 shell 运行,则 setopt 和 pipestatus 并不能作为该 shell 自身管道控制的可移植替代品。请保持解释器与语法一致,并在执行后续工作之前保留管道结果。
来源
16 Options (zsh) — Z Shell 手册
6 Shell Grammar (zsh) — Z Shell 手册
15 Parameters (zsh) — Z Shell 手册
在 Mac 上的“终端”中更改默认 Shell — Apple 支持