使用花括号创建 Bash 子shell

iru*_*var 38 bash subshell

根据将花括号之间的命令列表导致在当前外壳上下文要执行的列表。没有创建子shell

ps看到这个在行动

这是直接在命令行上执行的流程管道的流程层次结构。4398 是登录 shell 的 PID:

sleep 2 | ps -H;
  PID TTY          TIME CMD
   4398 pts/23   00:00:00 bash
   29696 pts/23   00:00:00   sleep
   29697 pts/23   00:00:00   ps
Run Code Online (Sandbox Code Playgroud)

现在遵循直接在命令行上执行的大括号之间的流程管道的流程层次结构。4398 是登录 shell 的 PID。它类似于上面证明所有内容都在当前 shell 上下文中执行的层次结构

{ sleep 2 | ps -H; }
   PID TTY          TIME CMD
    4398 pts/23   00:00:00 bash
    29588 pts/23   00:00:00   sleep
    29589 pts/23   00:00:00   ps
Run Code Online (Sandbox Code Playgroud)

现在,这是sleep管道中本身放置在大括号内时的流程层次结构(因此总共有两级大括号)

{ { sleep 2; } | ps -H; }
  PID TTY          TIME CMD
   4398 pts/23   00:00:00 bash
   29869 pts/23   00:00:00   bash
   29871 pts/23   00:00:00     sleep
   29870 pts/23   00:00:00   ps
Run Code Online (Sandbox Code Playgroud)

当文档指出大括号之间的命令在当前 shell 上下文中执行时,为什么bash必须创建一个子 shell 以sleep在第 3 种情况下运行?

Sté*_*las 31

在管道中,所有命令同时运行(它们的标准输出/标准输入通过管道连接),因此在不同的进程中。

cmd1 | cmd2 | cmd3
Run Code Online (Sandbox Code Playgroud)

所有三个命令都在不同的进程中运行,因此其中至少两个必须在子进程中运行。一些 shell 在当前 shell 进程中运行其中一个(如果内置类似read或者如果管道是脚本的最后一个命令),但bash在它们自己的单独进程中运行它们(除了lastpipe最近bash版本中的选项和在某些特定条件下) )。

{...}组命令。如果该组是管道的一部分,则它必须像一个简单的命令一样在单独的进程中运行。

在:

{ a; b "$?"; } | c
Run Code Online (Sandbox Code Playgroud)

我们需要一个 shell 来评估它a; b "$?"是一个单独的进程,所以我们需要一个子 shell。shell 可以通过不分叉进行优化,b因为它是在该组中运行的最后一个命令。有些 shell 会这样做,但显然不会bash


slm*_*slm 21

嵌套花括号似乎表示您正在创建一个额外的作用域级别,这需要调用一个新的子 shell。您可以在ps -H输出中使用 Bash 的第二个副本看到这种效果。

只有第一级大括号中规定的进程才会在原始 Bash shell 的范围内运行。任何嵌套的花括号都将在它们自己的作用域 Bash shell 中运行。

例子

$ { { { sleep 20; } | sleep 20; } | ps -H; }
  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5012 pts/1    00:00:00   bash
 5014 pts/1    00:00:00     bash
 5016 pts/1    00:00:00       sleep
 5015 pts/1    00:00:00     sleep
 5013 pts/1    00:00:00   ps
Run Code Online (Sandbox Code Playgroud)

去掉| ps -H混合只是为了我们可以看到嵌套的花括号,我们可以ps auxf | less在另一个 shell 中运行。

saml     29190  0.0  0.0 117056  3004 pts/1    Ss   13:39   0:00  \_ bash
saml      5191  0.0  0.0 117056  2336 pts/1    S+   14:42   0:00  |   \_ bash
saml      5193  0.0  0.0 107892   512 pts/1    S+   14:42   0:00  |   |   \_ sleep 20
saml      5192  0.0  0.0 107892   508 pts/1    S+   14:42   0:00  |   \_ sleep 20
saml      5068  0.2  0.0 116824  3416 pts/6    Ss   14:42   0:00  \_ bash
saml      5195  0.0  0.0 115020  1272 pts/6    R+   14:42   0:00      \_ ps auxf
saml      5196  0.0  0.0 110244   880 pts/6    S+   14:42   0:00      \_ less
Run Code Online (Sandbox Code Playgroud)

但是等等还有更多!

如果您取出管道并使用这种形式的命令,我们会看到您实际期望的内容:

$ { { { sleep 10; } ; { sleep 10; } ; sleep 10; } } | watch "ps -H"
Run Code Online (Sandbox Code Playgroud)

现在在生成的观察窗口中,我们每 2 秒获得一次正在发生的事情的更新:

这是第一个sleep 10

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5678 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5681 pts/1    00:00:00     watch
 5682 pts/1    00:00:00       ps
Run Code Online (Sandbox Code Playgroud)

这是第二个sleep 10

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5691 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5694 pts/1    00:00:00     watch
 5695 pts/1    00:00:00       ps
Run Code Online (Sandbox Code Playgroud)

这是第三个sleep 10

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5704 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5710 pts/1    00:00:00     watch
 5711 pts/1    00:00:00       ps
Run Code Online (Sandbox Code Playgroud)

请注意,尽管在花括号的不同嵌套级别调用了所有三个睡眠,但实际上都在 Bash 的 PID 5676 内。所以我相信你的问题是使用| ps -H.

结论

使用| ps -H(即管道)会导致额外的子外壳,因此在尝试询问正在发生的事情时不要使用该方法。


Mir*_*kár 7

我将发布我的测试结果,这使我得出结论,当且当它是管道的一部分时,bash 为组命令创建了一个子 shell ,这类似于人们会调用一些也会被调用的函数在子壳中。

$ { A=1; { A=2; sleep 2; } ; echo $A; }
2

$ { A=1; { A=2; sleep 2; } | sleep 1; echo $A; }
1
Run Code Online (Sandbox Code Playgroud)