为什么 shell 命令“{ command1; command2: } &" 打开子shell?

Why does shell command “{ command1; command2: } &" open a subshell?

众所周知,将命令列表放在花括号之间会导致列表在当前 shell 上下文中执行。没有创建 subshell。但是在“{}”之后使用“&”时,为什么会创建两个subshell? pid 1002 和 1003.

{
    ./a.out
} &

sleep 19

当使用“./a.out &”时,只会创建一个子shell。 pid 17358.

./a.out &
sleep 19

为什么?

如果命令被控制运算符 & 终止,shell 将在 后台执行命令(或 {...} 中包含的命令列表)(或异步)在 subshell.

shell不等待命令完成,return状态为0。

在 C 程序中,它是通过执行 fork() 然后 execvp() 系统调用来完成的。


更新: 基于下面的评论和更新的问题。这是正在发生的事情。

当你 运行:

./a.out &

BASH 直接在后台 运行s a.out 作为 运行ning 二进制文件 a.out 不需要单独的 shell 进程.

当你 运行:

{ ./a.out; } &

BASH 必须首先分叉并创建一个 subshell,因为您可以在 {...} 中包含一系列命令,然后是新分叉的 subshell 运行s a.out 在一个单独的进程中。所以并不是 BASH 为此创建了 2 个 subshell。只有一个 subshell 被创建,你看到的第二个 pid 是 a.out.

列表的后台执行使用子shell,因为需要等待该列表的每个成员和运行下一个成员。列表后台运行后,父级 shell 需要可用于新命令;它也无法管理后台列表。 bash 一次只能做一件事。因此,要使后台列表正常工作,它 运行 是一个子 shell。

请注意,您可以拒绝后台列表,它会保持 运行ning,表明子shell 正在工作:

$ {
> sleep 1; sleep 2; sleep 3; sleep 4; sleep 5
> } &
$ disown
$ ps -f | grep sleep
dave     31845 31842  0 03:50 pts/1    00:00:00 sleep 3 
dave     31849 31771  0 03:50 pts/1    00:00:00 grep sleep

您甚至可以注销,子shell 将继续列表中的 运行ning 进程。

当您后台运行单个命令时,不需要子shell,因为shell 在拥有运行 命令后就没有更多的工作要做。

在您的示例中,第二个附加 bash 子进程 PID 1002 似乎是您正在执行的脚本。这与列表后台机制无关(至少在概念上);单独文件中的任何脚本都有自己的 bash 进程。