考虑以下命令:
exit > /dev/null
exit | cat
在一些 shell(ksh、bash、(d)ash)上,行为是相同的:第一个命令导致 shell 立即退出,而第二个命令没有可见行为。
我得出的结论是,第一个命令不涉及 fork(2),而第二个命令涉及两个(一个执行exit
,另一个 execve(2) 执行cat
)。
我查看了 POSIX 规范第 2.14 节,但没有找到任何明确说明这一点的内容。
POSIX 是否指定某些命令不能 fork(2) 而其他一些命令必须?是否为标准可接受的第一个命令生成子 shell?
我知道( exit )
永远不应该退出当前的 shell,因为括号会产生一个实际执行exit
命令的子 shell,这意味着子 shell 将立即退出。但是,我不确定重定向和管道,因为这里没有括号。
我问这个问题是因为在最近的课程实验室中,我们被告知要实现一个最小的 Unix shell。对于附加点,我们可以实现许多“可选功能”。这些“可选功能”之一是组合重定向和流水线。我们有一些不同的实现,我相信有些实现比其他的更接近 POSIX 的指定行为,因此我想知道实际行为是如何指定的。
好吧(退出),
以及(2.12. Shell 执行环境)
因此
exit
,管道中的管道在其自己的执行环境/子外壳中运行,并且仅退出该环境,而简单命令中的管道运行在exit > /dev/null
主外壳环境中。(如评论中所述,重定向根本不会影响它。)请注意,第二个引号中间的部分意味着某些 shell 可能会在主环境中运行管道的所有命令,因此即使在这种情况下也会退出整个 shell。在实践中,这更常见于管道的最后一个命令。
在 Bash 中
lastpipe
,例如:但
不打印任何东西。
发生这种情况是因为
exit
在子shell 中执行 forexit | cat
而不是 forexit > /dev/null
。 从子 shell 退出不会从主 shell 退出:但是POSIX 标准第 2.12 节规定了 subshell 与否的具体区别——全文引用相关段落:
这里
exit | cat
符合描述:因此通常会在子shell中执行。但是,这可能会让您失望:
...这意味着它不能保证在所有外壳中。我以前必须调试代码,其中一个实现在当前 shell 中执行管道的右侧,允许以下内容在 KSH 的某些实现上工作,但在其他实现上不行:
所以总是假设管道可能会产生一个子shell,但不要依赖它。