Answers:
括号总是开始一个子shell。发生的是bash检测到sleep 5
该子shell执行的最后一条命令,因此它调用exec
而不是fork
+ exec
。该sleep
命令将在同一过程中替换子外壳。
换句话说,基本情况是:
( … )
创建一个子shell。原始进程调用fork
和wait
。在子过程中,它是一个子shell:
sleep
是需要子过程的子过程的外部命令。子外壳调用fork
和wait
。在子子流程中:
exec
。exit
。wait
在子外壳中完成。wait
在原始过程中完成。优化是:
( … )
创建一个子shell。原始进程调用fork
和wait
。在子进程中,它是一个子外壳,直到调用exec
:
sleep
是一个外部命令,这是此过程需要做的最后一件事。exec
。exit
。wait
在原始过程中完成。当您在调用之后添加其他内容时sleep
,需要保留子外壳,因此不会发生这种优化。
当您在调用之前添加其他内容时sleep
,可以进行优化(而ksh可以进行优化),但是bash不能进行优化(此优化非常保守)。
fork
+ exec
,而外部命令则调用它。如果不进行任何优化,fork
则会调用该子shell,并调用另一个用于外部命令。我在回答中添加了详细的流程描述。
(...)
(基本情况下)情况下,exec
是否调用取决于子外壳程序是否具有要执行的外部命令,而在执行任何外部命令的情况下必须存在fork + exec
。
date
Shell中的命令进行?
strace -f -e clone,execve,write bash -c 'date'
和strace -f -e clone,execve,write bash -c 'date; true'
从《高级Bash编程指南》中:
“通常,脚本中的外部命令会派生一个子进程,而Bash内置的命令则不会。因此,与它们的外部命令相比,内置的命令执行更快,使用的系统资源更少。”
还有一点点向下:
“嵌入在括号之间的命令列表作为子外壳运行。”
例子:
[root@talara test]# echo $BASHPID
10792
[root@talara test]# (echo $BASHPID)
4087
[root@talara test]# (echo $BASHPID)
4088
[root@talara test]# (echo $BASHPID)
4089
使用OPs代码的示例(由于我不耐烦,所以睡眠时间较短):
echo $BASHPID
sleep 2
(
echo $BASHPID
sleep 2
echo $BASHPID
)
输出:
[root@talara test]# bash sub_bash
6606
6608
6608
sleep
中运行(由于它是内置的而不是子外壳程序的子进程,因此可能在子外壳程序的进程中运行)。但是,无论如何,我都希望存在一个子外壳,即父Bash进程下的Bash子进程。对于上面的代码段B,似乎并非如此。
sleep
它似乎不是内置的,所以我希望sleep
两个代码片段中的第二个调用都可以在subshell进程的子进程中运行。
$BASHPID
变量来破坏代码的自由。可悲的是,您这样做的方式并没有给我带来整个故事。请参阅答案中我添加的输出。
@Gilles答案的附加说明。
正如吉勒斯所说: The parentheses always start a subshell.
但是,此类子外壳程序具有的数字可能会重复:
$ (echo "$BASHPID and $$"; sleep 1)
2033 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2040 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2047 and 31679
如您所见,$$不断重复,这与预期的一样,因为(执行此命令以找到正确的man bash
行):
$ LESS=+/'^ *BASHPID' man bash
BASHPID
扩展为当前bash进程的进程ID。在某些情况下,这与$$不同,例如不需要重新初始化bash的子外壳。
也就是说:如果未重新初始化外壳,则$$是相同的。
或与此:
$ LESS=+/'^ *Special Parameters' man bash
特殊参数
$扩展到外壳的进程ID。在()子外壳程序中,它扩展为当前外壳程序的进程ID,而不是子外壳程序。
的$$
是当前的壳(未子shell)的ID。
fork
子进程(以执行外部命令)fork + exec
。但您的第一段建议也fork + exec
将其称为subshell。我在这里错了吗?