用花括号创建Bash子外壳


31

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

ps看到这个在行动

这是直接在命令行上执行的流程管道的流程层次结构。4398是登录外壳程序的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

现在遵循直接在命令行上执行的花括号之间的流程管道的流程层次结构。4398是登录外壳的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

现在,这是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

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


有趣的是,我猜这是因为在第三种情况下,内部组是管道的一部分,因此它在子外壳中执行,例如,作为管道一部分的任何其他函数调用都将在子外壳中执行。是否有意义?
MiroslavKoškár2014年

2
我不会说“ shell必须”只是因为它确实...流水线不在 shell上下文中执行。如果管道仅由外部命令组成,那么创建子流程就足够了。{ sleep 2 | command ps -H; }
Hauke Laging

Answers:


26

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

cmd1 | cmd2 | cmd3

这三个命令都在不同的进程中运行,因此至少有两个必须在子进程中运行。一些shell在当前shell进程中运行其中一个(如果是内置的,read或者管道是脚本的最后命令),但bash都在各自独立的进程中运行它们(除非lastpipe在最新bash版本中使用该选项,并且在某些特定条件下运行) )。

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

在:

{ a; b "$?"; } | c

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


这三个命令都在不同的进程中运行,因此其中至少有两个必须在子shell中运行。 ”在这种情况下,为什么需要一个子shell?父外壳不能生成树进程吗?或者,当您说“ 必须在子Shell中运行 ”时,是否表示Shell将分叉自身,然后对每个cmd1 cmd2和cmd3执行?如果执行此操作,bash -c "sleep 112345 | cat | cat "我只会看到创建了一个bash,然后看到了三个bash,而没有其他交织的子网格。
哈坎巴巴(Hakan Baba)

我们需要一个外壳程序来评估a; b“ $?”是一个单独的过程,因此我们需要一个子外壳程序。 ”您还可以扩展推理吗?为什么我们需要一个子框架来理解这一点?要了解什么呢?我认为需要解析,但是还有什么呢?。父外壳不能解析a; b "$?"吗?确实确实需要子框架,还是在bash上进行设计决策/实现?
哈坎巴巴(Hakan Baba)

@HakanBaba,为了避免潜在的混乱,我将其更改为“子进程”。
斯特凡Chazelas

1
@HakanBaba,解析在父级中完成(读取代码的过程,除非将代码传递给eval,否则执行解释器的过程),但是评估(运行第一个命令,等待它,运行第二个)在孩子身上完成,将stdout连接到管道。
斯特凡Chazelas

{ sleep 2 | ps -H; }父bash中看到sleep 2需要fork / exec。但是换句话说,{ { sleep 2; } | ps -H; }在父bash中看到{ sleep 2; }了一些bash代码。看起来父级可以处理fork / exec,sleep 2但是递归产生新的bash来处理遇到的bash代码。这是我的理解,有意义吗?
哈坎巴巴(Hakan Baba)

19

嵌套花括号似乎表示您正在创建其他范围的作用域,这需要调用新的子外壳。您可以在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

| 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

但是,等等!

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

$ { { { sleep 10; } ; { sleep 10; } ; sleep 10; } } | watch "ps -H"

现在,在出现的监视窗口中,我们每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

这是第二个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

这是第三个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

请注意,尽管在不同的花括号嵌套级别调用了所有三个睡眠,但实际上它们确实保留在Bash的PID 5676中。因此,我相信您的问题是使用造成的| ps -H

结论

使用的| ps -H(即管)引起的附加子壳,因此试图询问发生了什么事时,不要使用该方法。


因此,“只有第一级花括号中规定的过程才在原始Bash shell的范围内运行。”
xealits 2014年

@xealits-您是在问我一个跟进问题吗?
slm

如我所见,@ slm只是强调答案的重点。大括号第一级中的命令在当前shell中运行,嵌套大括号创建新的shell。括号的不同之处在于,在第一层立即创建子外壳。如果我弄错了-纠正我。但是,正如其他人指出的那样,最初的问题还有管道。这样就创建了单独的流程。花括号在用于单独的过程时必须创建外壳。因此,很可能是造成该问题的原因。
xealits 2014年

现在,在重新阅读您的文章之后,我发现我错了-嵌套语句仅涉及管道的情况。因此,花括号永远不会创建新的壳,除非您用它们包装了一个单独的过程-然后它们必须这样做。
xealits 2014年

@xealits-是的。
slm

7

我将发布测试结果,这使我得出结论,当且当bash 是管道的一部分时,bash才能为group命令创建一个子shell ,这类似于有人调用某个函数(也将被调用)在子外壳中。

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

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

我的A也正在显示此内容。
slm
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.