为什么 bash while 循环在管道到终止的子命令时不退出?

ste*_*n.z 13 linux bash shell command-line bash-scripting

为什么下面的命令不退出?而不是退出,循环无限期地运行。

虽然我使用更复杂的设置发现了这种行为,但该命令的最简单形式简化为以下形式。

不退出:

while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
Run Code Online (Sandbox Code Playgroud)

上面没有错别字。每个'|' 是一个管道。“exit 1”代表另一个运行并退出的进程。

我希望“exit 1”在 while 循环中引起 SIGPIPE(在没有读取器的管道上写入)并导致循环中断。但是,循环继续运行。

为什么命令不停止?

jll*_*gre 14

这是由于实施中的选择。

在 Solaris 上运行相同的脚本ksh93会产生不同的行为:

$ while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
cat: write error [Broken pipe]
Run Code Online (Sandbox Code Playgroud)

触发问题的是内部管道,没有它,循环退出任何外壳/操作系统:

$ while /usr/bin/true ; do echo "ok" ; done | exit 1
$
Run Code Online (Sandbox Code Playgroud)

cat 正在 bash 下获取 SIGPIPE 信号,但外壳无论如何都在迭代循环。

Process 5659 suspended
[pid 28801] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28801] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28801 detached
Process 28800 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28802 attached
Process 28803 attached
[pid 28803] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
Process 5659 suspended
[pid 28803] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28803 detached
Process 28802 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28804 attached
Process 28805 attached (waiting for parent)
Process 28805 resumed (parent 5659 ready)
Process 5659 suspended
[pid 28805] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28805] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28805 detached
Process 28804 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Run Code Online (Sandbox Code Playgroud)

Bash文档指出:

shell在返回值之前等待管道中的所有命令终止。

Ksh文档指出:

每个命令,可能除了最后一个,都作为一个单独的进程运行;shell等待最后一个命令终止。

POSIX指出:

如果管道不在后台(参见异步列表),shell将等待管道中指定的最后一个命令完成,也可能等待所有命令完成。