POSIX sh: 复合命令如何与管道一起工作?

POSIX sh: how do compound commands work with pipes?

我试图了解顺序命令组(在 GNU bash 手册中称为“列表”)如何与 POSIX sh 定义或其实现中的管道一起工作。例如下面的代码:

if test-expr1; then
  cmd1; cmd2; cmd3; ...
fi | { cmd4; cmd5; ...; } | cmd6

此处,管道有两个连续的“列表”和一个命令,其中 cmd2cmd1 完成后执行,cmd5cmd4 后执行。但是,所有三个都是 运行 并行的,因此管道不会阻塞。

我的第一个猜测是,对于这些“列表”中的每一个,都会创建一个子shell,它有一个单独的 PID,它按顺序执行所有命令,但不会阻止 shell 从产生其他进程 — 如果 shell 开始等待 cmd1 完成,就会发生这种情况。但是,这是不正确的,因为 echo $$ 即使在管道命令列表中仍然报告相同的 PID。

问题是:究竟如何使用 fork()/waitpid() 来实现正确的行为?

if test-expr1; then
  cmd1; cmd2; cmd3; ...
fi | { cmd4; cmd5; ...; } | cmd6

将在解析树中呈现为:

                                 _______________|
                                /                \
               ________________|___               \
              /                    \               \
          IF__________________      \               \
         /  \                 \      \               \
test-expr1   cmd1; cmd2; cmd3  (nil)  cmd4; cmd5;     cmd6

看着树叶:​​

  1. test-expr1: 可能会分叉,如果它涉及 运行ning 命令,否则不需要。
  2. 命令1; ...:可能会分叉,除非它们是内部结构。一个分叉 shell 可以 运行 这些顺序
  3. 命令4; cmd5:可能会分叉,除非是内部结构。一个分叉 shell 可以 运行 这些 seq.
  4. cmd6:可能会分叉,除非内部。一个分叉的 shell 可以执行这个。

IF statemnet (1, 2) 通过管道传输到 (3)。 (3) 通过管道传输到 4。因此,如果您的命令是:

if /bin/echo You; then /bin/echo say; /bin/echo hi; fi | { cat -; echo I; echo say; echo Lo;  } | cat -;

你得到输出:

You
say
hi
I
say
Lo

This was incorrect, though, as echo $$ still reports the same PID even in piped command lists.

这是正确的,但您的检查方法不正确。来自 bash manual:

($$) Expands to the process ID of the shell. In a () subshell, it expands to the process ID of the invoking shell, not the subshell.

因此您检查了调用 shell 在所有子shell 中是否相同。是的。