somefile
包含这个:
aaa
bbb
ccc
这是可以在完成所有迭代之前退出的子 shell。在我的特定情况下是,while
但也可能for
是until
:
cat "somefile" | while read -r line
do
echo "$line"
if [ "$line" = "bbb" ]
then
exit 2
fi
done
exit_code="$?"
if [ "$exit_code" -ne 0 ]
then
exit
fi
echo "I'm here"
问题是最后一行I'm here
被打印出来了。我的目的是完全退出到终端。但是我不确定这是否正确并且是否适用于所有 shell。这甚至是 POSIX 吗?有没有提到这在 POSIX 中是如何工作的?
我询问过几个人工智能引擎,每个引擎都有不同的答案:
- 副驾驶:表示 exit_code 将始终为 0,因为这是 cat 的退出代码。可以是任何其他命令。
- ChatGPT:表示 exit_code 将是退出后的数字,比如这里的 2,因为它会传播。
- Gemini:表示 exit_code 将始终为 0,因为 $? 获取最后一条指令的退出代码,该代码
done
始终为 0。
有什么帮助吗?我尝试过在 bash 中检测退出代码,但不确定这是否是某些特定的 bash 配置(例如 pipefail 或它在任何 shell 中工作的方式(例如在 UNIX 中也是如此)。
如果选项¹关闭(默认),则管道的退出状态是最右侧组件的退出状态;如果选项¹打开
pipefail
,则管道的退出状态为最右侧具有非零退出状态的组件的退出状态。pipefail
例如,
(exit 12) | (exit 5) | (exit 0)
如果没有 , 的退出状态将为 0pipefail
,而如果有 , 的退出状态将为 5。现在,在
sh
管道中,POSIX 未指定管道的最后一个(最右边)组件是否在子 shell 中运行,并且实际上,它因sh
实现而异。因此,根据 shell 的不同
exit 2
,如果运行,则将完全退出脚本,或者仅退出运行该循环的子 shell 。在后一种情况下,无论选项是否打开,while
管道的退出状态都将为 2 。pipefail
不过,在这里你可以做类似的事情:
那是调用文本处理实用程序进行文本处理,而不是数百次调用(假设输入数百行)不适当的工具,例如
read
,,。echo
[
参见:
¹ ksh 扩展现在可在大多数
sh
实现中找到,并且自标准 2024 年版以来由 POSIX 指定,但尚未在所有实现中找到,特别是dash
。根据POSIX:
最后一个命令是
while
循环。循环的退出状态while
始终为 0,除非运行它的 shell 未完成循环就退出。所有类似 sh 的 shell 的行为都类似。1
但是,部分行为取决于 shell。大多数 sh 类 shell(包括经典的 Bourne shell、bash、dash 和 mksh)都在子 shell 中运行管道的每一侧。如果
exit 2
达到,它会退出管道右侧的子 shell,因此行exit_code="$?"
设置exit_code
为 2。在 ATT ksh 和 zsh 中,管道的右侧在主 shell 中执行,因此如果exit 2
达到,整个脚本将以状态 2 退出,并且行exit_code="$?"
永远不会执行。唯一
echo "I'm here"
可以达到的方法是,如果文件不包含完全包含的行bbb
。如果您使用的是 Windows,请确保文件具有 Unix 行尾,而不是 Windows 行尾。Windows 行尾由两个字符 CR LF 组成,而 Unix 行尾仅为 LF,因此 Unix 工具会将 Windows 行视为包含预期文本加上 CR 字符。
1 为了完整性:某些 shell 可以配置为,如果任何命令通过运行 失败,则为管道赋予非零状态
set -o pipefail
。使用此配置,如果while
循环返回 0,则将获得 的退出状态cat
,如果可以读取给定的文件,则退出状态为 0。