什么时候命令替换产卵不是孤立相同的命令更多的子shell? [英] When does command substitution spawn more subshells than the same commands in isolation?

查看:95
本文介绍了什么时候命令替换产卵不是孤立相同的命令更多的子shell?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

昨天有人向我建议bash中使用命令替换导致不必要的子shell被催生。该建议是具体到这个用例

Yesterday it was suggested to me that using command substitution in bash causes an unnecessary subshell to be spawned. The advice was specific to this use case:

# Extra subshell spawned
foo=$(command; echo $?)

# No extra subshell
command
foo=$?

,尽我所能的身影,这似乎对这种用例是正确的。然而,快速搜索尝试验证这导致的混乱和矛盾的建议投注。这似乎流行的观点认为命令替换所有使用将产生一个子shell。例如:

As best I can figure this appears to be correct for this use case. However, a quick search trying to verify this leads to reams of confusing and contradictory advice. It seems popular wisdom says ALL usage of command substitution will spawn a subshell. For example:

该命令替换扩展到命令的输出。 这些命令在子shell 执行,它们的标准输出数据是什么替代语法扩展到。 (

The command substitution expands to the output of commands. These commands are executed in a subshell, and their stdout data is what the substitution syntax expands to. (source)

这似乎很简单,除非你不断挖掘,在这种情况下,你会开始寻求建议的参考,这是情况并非如此。

This seems simple enough unless you keep digging, on which case you'll start finding references to suggestions that this is not the case.

命令替换的不一定调用子shell ,在大多数情况下不会。它保证了唯一的事情是乱序的评价:这只是第一次评估替代内部的前pressions,然后计算使用替换的结果周围的声明。 (

Command substitution does not necessarily invoke a subshell, and in most cases won't. The only thing it guarantees is out-of-order evaluation: it simply evaluates the expressions inside the substitution first, then evaluates the surrounding statement using the results of the substitution. (source)

这似乎是合理的,但它是真的吗? 这个答案到子shell相关问题我通风报信关闭该男人庆典有这个需要注意的:

This seems reasonable, but is it true? This answer to a subshell related question tipped me off that man bash has this to note:

在管道中的每个命令都作为单独的进程中执行(即,在一个子shell)。

Each command in a pipeline is executed as a separate process (i.e., in a subshell).

这使我的主要问题。 究竟什么会导致命令替换产卵,不会被催生反正孤立?执行相同的命令,一个子shell

This brings me to the main question. What, exactly, will cause command substitution to spawn a subshell that would not have been spawned anyway to execute the same commands in isolation?

请考虑以下情况并说明哪些需额外子shell的开销:

Please consider the following cases and explain which ones incur the overhead of an extra subshell:

# Case #1
command1
var=$(command1)

# Case #2
command1 | command2
var=$(command1 | command2)

# Case #3
command1 | command 2 ; var=$?
var=$(command1 | command2 ; echo $?)

做这些对承担相同数量的子shell来执行?是否有与庆典在实现POSIX有区别吗? 还有没有其他的情况下,在那里运行同一组隔离命令使用命令替换会催生一个子shell不会?

推荐答案

更新和警告

这个回答有,我自信地声称事情,竟然是不正确的困扰的过去。我相信它有它的价值的电流的形式,但请大家帮我消除其它错误(或说服我,它应该完全删除)。

This answer has a troubled past in that I confidently claimed things that turned out not to be true. I believe it has value in its current form, but please help me eliminate other inaccuracies (or convince me that it should be deleted altogether).

我的大幅修订 - 而且大多烧毁 - 这个答案 @kojiro后指出,我的测试方法是有缺陷的(我最初使用 PS 寻找子进程,但是这太慢了,总是发现他们);一个新的测试方法描述如下。

I've substantially revised - and mostly gutted - this answer after @kojiro pointed out that my testing methods were flawed (I originally used ps to look for child processes, but that's too slow to always detect them); a new testing method is described below.

我原本声称,并不是所有的子shell的bash在自己的子进程中运行,但事实证明是不正确的。

I originally claimed that not all bash subshells run in their own child process, but that turns out not to be true.

作为他的回答@kojiro状态的部分的贝壳 - 比其他庆典 - 有时会避免子shell子进程的创建,于是,的一般的在世界说话贝壳,人们不应该假设一个子shell意味着一个子进程。

As @kojiro states in his answer, some shells - other than bash - DO sometimes avoid creation of child processes for subshells, so, generally speaking in the world of shells, one should not assume that a subshell implies a child process.

至于在猛砸OP的情况下(假定命令{N} 实例的简单的命令的):

As for the OP's cases in bash (assumes that command{n} instances are simple commands):

# Case #1
command1         # NO subshell
var=$(command1)  # 1 subshell (command substitution)

# Case #2
command1 | command2         # 2 subshells (1 for each pipeline segment)
var=$(command1 | command2)  # 3 subshells: + 1 for command subst.

# Case #3
command1 | command2 ; var=$?         # 2 subshells (due to the pipeline)
var=$(command1 | command2 ; echo $?) # 3 subshells: + 1 for command subst.;
                                     #   note that the extra command doesn't add 
                                     #   one

它看起来像使用命令替换( $(...))的总是添加在bash一个额外的子shell - 一样封闭的任何命令在(...)

It looks like using command substitution ($(...)) always adds an extra subshell in bash - as does enclosing any command in (...).

我相信,但我不能确定这些结果是否正确;以下是我测试过(bash的51年3月2日在OS X 10.9.1) - 请告诉我,如果这种方法是有缺陷的

I believe, but am not certain these results are correct; here's how I tested (bash 3.2.51 on OS X 10.9.1) - please tell me if this approach is flawed:


  • 确信只有2交互式的bash炮弹运行:一个运行的命令,其他监测

  • 在第二壳我监控的叉()要求在须藤dtruss -t叉-f -p {} pidOfShell1 1号(即 -f 还有必要追查叉()所谓的传递性,即包括那些由子shell本身)创建。

  • 用于仅内建(无操作)的测试命令(避免得过且过额外叉图片() 呼吁外部可执行文件);具体做法是:

  • Made sure only 2 interactive bash shells were running: one to run the commands, the other to monitor.
  • In the 2nd shell I monitored the fork() calls in the 1st with sudo dtruss -t fork -f -p {pidOfShell1} (the -f is necessary to also trace fork() calls "transitively", i.e. to include those created by subshells themselves).
  • Used only the builtin : (no-op) in the test commands (to avoid muddling the picture with additional fork() calls for external executables); specifically:



  • $(:)

  • :|

  • $(:|:)

  • :| :;

  • $(:|:;:)

  • :
  • $(:)
  • : | :
  • $(: | :)
  • : | :; :
  • $(: | :; :)

只算包含一个非零PID(为每个子进程还报告 fork()的那些 dtruss 输出线创建它的电话,但具有PID 0)。

Only counted those dtruss output lines that contained a non-zero PID (as each child process also reports the fork() call that created it, but with PID 0).

下面是我仍然认为是正确的,从我原来的职位:当bash创建子shell

Below is what I still believe to be correct from my original post: when bash creates subshells.

庆典在下列情况下创建子shell:


  • 的Ex pression用括号((...)

    • 除了的直接在 [...] ,其中括号仅用于逻辑分组。

    • for an expression surrounded by parentheses ( (...) )
      • except directly inside [[ ... ]], where parentheses are only used for logical grouping.

      • 每个的管道段( | ),其中的第一个一个

        • 请注意,每个的壳层涉及的是一个克隆的原始的外壳中的内容的(过程的角度来看,子shell可以从分叉条款其他子shell(执行的命令之前))。结果
          因此,在早期​​的管线段子shell的修改不会影响到后来者。结果
          (根据设计,在管道命令是推出的同时的 - 测序只发生通过连接的标准输入/输出管道)

        • bash的4.2 + 有shell选项 lastpipe (默认关闭),这将导致的最后的管道段不要在一个子shell中运行。

        • for every segment of a pipeline (|), including the first one
          • Note that every subshell involved is a clone of the original shell in terms of content (process-wise, subshells can be forked from other subshells (before commands are executed)).
            Thus, modifications of subshells in earlier pipeline segments do not affect later ones.
            (By design, commands in a pipeline are launched simultaneously - sequencing only happens through their connected stdin/stdout pipes.)
          • bash 4.2+ has shell option lastpipe (OFF by default), which causes the last pipeline segment NOT to run in a subshell.

          • 有关命令替换( $(...)

          有关进程替换(≤(...)


          • 通常创建的 2 的子shell;在简单的命令的情况下, @konsolebox想出了一个技术仅创建 1 的:prePEND与 EXEC &LT简单的命令;(EXEC ...))。

          • typically creates 2 subshells; in the case of a simple command, @konsolebox came up with a technique to only create 1: prepend the simple command with exec (<(exec ...)).

          • 后台执行(&安培;

          • background execution (&)

          结合这些构建体将导致在多个子外壳

          Combining these constructs will result in more than one subshell.

          这篇关于什么时候命令替换产卵不是孤立相同的命令更多的子shell?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆