# Extra subshell spawned foo=$(command; echo $?) # No extra subshell command foo=$?
最好我可以认为这个用例似乎是正确的。然而,一个试图验证这一点的快速搜索会导致混乱和矛盾的建议。似乎流行的智慧说,所有使用命令替换将产生一个subshell。例如:
The command substitution expands to the output of commands. These commands are executed in a subshell,and their stdout data is what the substitution Syntax expands to. (07001)
这似乎很简单,除非你继续挖掘,在这种情况下,您将开始查找关于不是这样的建议的参考。
Command substitution does not necessarily invoke a subshell,and in most cases won’t. The only thing it guarantees is out-of-order evaluation: it simply evaluates the expressions inside the substitution first,then evaluates the surrounding statement using the results of the substitution. (07002)
这似乎是合理的,但这是真的吗? This answer到一个subshell相关的问题提示我,那个男人bash有这个要注意:
Each command in a pipeline is executed as a separate process (i.e.,in a subshell).
这带给我主要的问题。究竟是什么,将导致命令替换产生一个不会被产生的subshell,无论如何孤立地执行相同的命令?
请考虑以下情况,并解释哪些引起了额外的子shell的开销:
# Case #1 command1 var=$(command1) # Case #2 command1 | command2 var=$(command1 | command2) # Case #3 command1 | command 2 ; var=$? var=$(command1 | command2 ; echo $?)
这些对中的每一个是否需要执行相同数量的子shell? POSIX与bash实现有区别吗?是否还有其他情况下,使用命令替换会产生一个subshell,其中运行相同的命令集在孤立不会?
这个答案有一个困扰的过去,我自信地声称事实证明不是真的。我相信它现在有价值,但请帮助我消除其他不准确之处(或说服我完全删除它)。
我已经大量修改了 – 大部分是内脏的 – 这个答案在@kojiro指出我的测试方法是有缺陷的(我最初使用ps来寻找子进程,但这太慢了,总是会发现它们);下面描述一种新的测试方法。
我原来声称并不是所有的bash子shell都运行在自己的子进程中,但事实证明不是真的。
正如@kojiro在他的回答中所说的,除了bash之外,有些shell有时避免为子shell创建子进程,所以一般来说在shell的世界中,不应该假设subshell意味着子进程。
对于bash中OP的情况(假定命令{n}实例是简单的命令):
# Case #1 command1 # NO subshell var=$(command1) # 1 subshell (command substitution) # Case #2 command1 | command2 # 2 subshells (1 for each pipeline segment) var=$(command1 | command2) # 3 subshells: + 1 for command subst. # Case #3 command1 | command2 ; var=$? # 2 subshells (due to the pipeline) var=$(command1 | command2 ; echo $?) # 3 subshells: + 1 for command subst.; # note that the extra command doesn't add # one
看起来像使用命令替换($(…))总是在bash中添加一个额外的subshell,就像在(…)中包含任何命令一样。
我相信,但不能肯定这些结果是正确的;这是我如何测试(在OS X 10.9.1上的bash 3.2.51) – 请告诉我这种方法是否有缺陷:
确保只有2个交互式bash shell正在运行:一个运行命令,另一个用于监视。
>在第二个shell中,我使用sudo dtruss -t fork -f -p {pidOfShell1}来监视fork()调用(-f是必要的,也可以跟踪fork()调用“过渡”,即包括创建的通过子壳本身)。
>只在测试命令中使用内置的(no-op)(以避免用额外的fork()调用外部可执行文件来混淆图片);特别:
>:
> $(:)
>:| :
> $(:|