在 Bash 中调用 subshel​​l 的规则?

bas*_*ful 30 shell bash shell-script subshell

我似乎误解了创建子 shell 的 Bash 规则。我认为括号总是会创建一个子shell,它作为自己的进程运行。

然而,情况似乎并非如此。在代码片段 A(如下)中,第二个sleep命令不在单独的 shell 中运行(由pstree在另一个终端中确定)。但是,在代码片段 B 中,第二个sleep命令确实在单独的 shell运行。片段之间的唯一区别是第二个片段在括号内有两个命令。

有人可以解释一下何时创建子shell的规则吗?

代码片段 A:

sleep 5
(
sleep 5
)
Run Code Online (Sandbox Code Playgroud)

代码片段 B:

sleep 5
(
x=1
sleep 5
)
Run Code Online (Sandbox Code Playgroud)

Gil*_*il' 25

括号总是开始一个子shell。发生的事情是 bash 检测到这sleep 5是该子shell执行的最后一个命令,因此它调用exec而不是fork+ exec。该sleep命令在同一进程中替换子shell。

换句话说,基本情况是:

  1. ( … )创建一个子外壳。原始进程调用forkwait。在子进程中,这是一个子shell:
    1. sleep是需要子进程的子进程的外部命令。子shell调用forkwait。在子进程中:
      1. 子进程执行外部命令 ? exec.
      2. 最终命令终止?exit.
    2. wait 在子shell中完成。
  2. wait 在原始过程中完成。

优化是:

  1. ( … )创建一个子外壳。原始进程调用forkwait。在子进程中,它是一个子shell,直到它调用exec
    1. sleep 是一个外部命令,它是这个进程需要做的最后一件事。
    2. 子进程执行外部命令?exec.
    3. 最终命令终止?exit.
  2. wait 在原始过程中完成。

当您在调用 之后添加其他内容时sleep,需要保留子外壳,因此不会发生这种优化。

当您在调用 之前添加其他内容时sleep,可以进行优化(ksh 会这样做),但 bash 不会这样做(这种优化非常保守)。


Tim*_*Tim 5

来自高级 Bash 编程指南

“一般来说,脚本中的外部命令会派生出一个子进程,而 Bash 内置命令不会。因此,与外部命令等效项相比,内置命令执行速度更快,使用的系统资源更少。”

再往下一点:

“括号之间嵌入的命令列表作为子shell运行。”

例子:

[root@talara test]# echo $BASHPID
10792
[root@talara test]# (echo $BASHPID)
4087
[root@talara test]# (echo $BASHPID)
4088
[root@talara test]# (echo $BASHPID)
4089
Run Code Online (Sandbox Code Playgroud)

使用 OPs 代码的示例(睡眠时间较短,因为我不耐烦):

echo $BASHPID

sleep 2
(
    echo $BASHPID
    sleep 2
    echo $BASHPID
)
Run Code Online (Sandbox Code Playgroud)

输出:

[root@talara test]# bash sub_bash
6606
6608
6608
Run Code Online (Sandbox Code Playgroud)

  • 感谢蒂姆的回复。我不确定它是否完全回答了我的问题。由于“嵌入在括号之间的命令列表作为子shell运行”,我希望第二个`sleep`在子shell中运行(可能在子shell的进程上运行,因为它是内置的,而不是子shell的子进程)。但是,无论如何,我都希望存在一个子shell,即父Bash 进程下的一个Bash 子进程。对于上面的代码段 B,情况似乎并非如此。 (2认同)