为什么单命令管道不在子shell中执行?

3
我的bash版本4.4.19的man页面上写着:
管道
管道是由一个或多个命令序列组成的,这些命令之间用控制操作符|或|&分隔。 管道的格式为:
[time [-p]] [ ! ] command [ [|||&] command2 ... ]
没错,这很有道理。然后,在该部分的后面:
在管道中的每个命令都作为单独的进程(即在子shell中)执行。
这也是合理的,并且很容易证明:
$ pwd
/Users/ravron
$ ls | grep src  # yes, src _does_ exist
src
$ cd src | true  # cd in subshell has no effect on parent session
$ pwd
/Users/ravron

然而,我注意到上面的管道定义是这样表述的,即甚至一个简单的命令也可以是一个管道。毕竟,在语法中,除了command以外的所有内容都是可选的:

[time [-p]] [ ! ] command [ [|||&] command2 ... ]

当然,仅在子shell中运行单个命令 - 在man bash术语中称为“简单命令” - 显然是无用的。例如,cd不起作用,变量设置也不起作用等等。但是,实际情况是,尽管简单命令与(相当退化的)管道的语法匹配,但它们并没有在子shell中执行,这与man页面相矛盾。以下是我的理解列表:
  • 管道的语法意味着简单命令也是管道。
  • man页面说管道中的命令在子shell中运行。
  • 简单命令不在子shell中运行。
我错过了什么,还是这只是man bash中一个不太重要的 bug?

2
如果您想要真正的规范文档,这些文档是由语言律师争论而成的,那么请访问http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html。请注意,在那里,单命令情况*被明确描述为管道。 - Charles Duffy
然而,请参见该标准的第2.9.1节,子节“命令搜索和执行”,免除内置行为对execve()通常要求在“单独的实用程序环境”中发生的要求。 - Charles Duffy
2个回答

3
手册不够精确。POSIX规范(第2.12节)更为明确(由我添加加粗):
此外,多命令管道的每个命令都在子shell环境中执行; 作为扩展,一个或多个管道中的所有命令都可以在当前环境中执行。所有其他命令都将在当前的shell环境中执行。
默认情况下,bash将实现每个子shell作为一个子进程。 (bash 4.2中引入的lastpipe选项允许最后一个命令在当前shell环境/进程中执行。)
shell的语法说明管道可以由一个或多个命令组成,但是shell的语义区分了一个命令的管道和两个或多个命令的管道。

1
手册从未定义“子shell”这个词。虽然您可能直觉地认为子shell始终是原始shell的子进程,但单个命令的退化情况使用原始shell进程作为子shell。
一些其他的shell更进一步,总是在原始shell中运行管道中的最后一个命令。

“每个管道中的命令都作为单独的进程执行”这句话不是与此相矛盾吗? - ravron
2
@ravron,要与其他管道组件分离,而不是与调用shell分离。 - Charles Duffy
请参见 https://dev59.com/4m7Xa4cB1Zd3GeqPlQqr。 - cdarke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接