【问题标题】:What happens when I use `&` with a function in a Bash script?当我在 Bash 脚本中将 `&` 与函数一起使用时会发生什么?
【发布时间】:2014-01-13 12:55:08
【问题描述】:

我正在用一个 shell 脚本调试一个奇怪的东西,想知道我是否误解了 Bash 的“fork”(&) 是如何工作的,可能是因为我通常通过终端在单个命令中使用它,例如:

[~me]$ someExecutable &

现在,在 shell 脚本中:

foo() {
   echo "logic"
}

bar() {
   echo "idic"
}

baz() {
   echo "katra"
}

foo &
bar
baz

我的意图是异步调用函数foo,然后让脚本的其余部分继续在原始进程上下文中执行。

当我执行这个示例脚本时,我得到预期的输出:

logic
idic
katra

(也就是说,只有对 barbaz 的单个明显调用;我一点也不担心三行输出的相对 顺序,我理解可能会有所不同)

但我可能对输出和脚本本身都有误解。如果是这样,那肯定可以解释我在真实代码中看到的奇怪之处,并且让我不必进一步挖掘。

是否有可能在子进程中,在调用foo 之后,分叉的shell 脚本会继续执行?这意味着我的 shell 脚本的其余部分将被执行两次

当我在 Bash 脚本中像这样使用 & 时会发生什么?

【问题讨论】:

    标签: linux bash shell


    【解决方案1】:

    在您的示例中,与号仅分叉对 foo() 的调用。它不是分叉整个脚本。只有 foo() 在单独的线程中运行,当 foo() 完成时退出。 bar() 和 baz() 继续在同一个线程中运行

    【讨论】:

    • 谢谢 - 这就是我所追求的!
    • +1 用于解决问题的核心。 Quibble:foo() 在(异步)subshel​​l 中运行,它是一个单独的进程
    【解决方案2】:

    bash 中的& 不是分叉。 它告诉 bash 在子 shell 中启动命令,并继续解析其他命令(而不是等待该命令完成)。

    因此,正如您所料,它在后台运行 'foo',并继续在顶层 shell 中处理 'bar' 和 'baz' 命令。调用 shell 没有分叉,但 'foo' 在后台运行。

    因此,“foo”的输出实际上可能发生在 bar 或 baz 之后,具体取决于其内容。但由于它使用的是内置 shell 的“echo”,因此在设置“foo”后台进程时, “echo”立即可用:因此,您很可能在调用 shell 传递到下一个命令之前看到它的输出,这显然是您的情况。 (但是 YMMV!如果“foo”调用了一个繁重的外部命令,它的输出很可能会在稍后出现)

    在终端中,这几乎是一样的:您可以将带有 shell 提示的终端窗口视为该 shell 的文件脚本,登录 shell 正在“在您键入时”读取。行为几乎相同。

    【讨论】:

    • 我不关心输出的顺序,我只得到三行。
    • 就像在终端中输入一样。但我猜你已经明白了 :) 这一切都跌跌撞撞地发现:& 不是分叉,即它不会分叉调用 shell(或者更确切地说是 shell+脚本)。 shell 在单独的进程中生成命令,然后在后台处理它,而不是等待它完成后再继续。参考web.mit.edu/gnu/doc/html/features_5.html
    • 复制那个。这意味着这些年来我的直觉一直是正确的,但我最初的错误仍然无法解释:(
    • 我们可以帮忙吗?想解释一下它是什么错误,并在它周围提取线条吗? [尝试找出问题所在,一开始不要担心变量的值]。给我们输出,为什么它是一个错误,以及导致它的潜在线路
    • 它在系统中太根深蒂固了,无法直接描述,我什至不完全确定症状是什么,除了一些看起来不对劲的原因,这些原因让我很怀疑,可能导致一些更具体的间歇性故障。我整天都在尝试为它构建一个测试用例;我还没有完成是我没有发布关于它的 SO 问题的原因之一;)另一个是,在构建了一个测试用例之后,我应该能够自己修复它。 :)
    【解决方案3】:

    & 字符告诉 shell 在后台执行一个命令(或一组命令)。

    下一个命令立即执行。

    没有执行脚本两次的危险,因此在这方面它与 C 的fork() 不同。

    【讨论】:

      【解决方案4】:

      除了控制运算符& 导致命令在子shell 1 中异步执行之外,您还可以使用Coprocesses 来控制输出。 p>

      而不是通过说来调用函数:

      foo &
      

      说:

      coproc fubar { foo; }
      

      然后阅读输出:

      cat <&"${fubar[0]}"
      

      这还允许您控制输出(是否需要在 barbaz 等其他函数之后或之前发出)。


      例如:

      foo() {
         echo "logic1"
         echo "logic2"
      }
      
      bar() {
         echo "idic"
      }
      
      baz() {
         echo "katra"
      }
      
      coproc fubar { foo; }
      bar
      baz
      cat <&"${fubar[0]}"
      

      会产生:

      idic
      katra
      logic1
      logic2
      

      【讨论】:

      • 虽然没有直接解决 OP 的问题,但最好了解协同进程(bash 4+)。
      【解决方案5】:

      实际上这个命令确实在后台运行你的函数 foo:

      foo &
      

      但是当你输入 barbaz 时,foo 已经完成了它的运行并在你的终端上打印了它的输出。

      但是,如果您像这样运行您的函数,那么您会注意到不同之处:

      foo & bar; baz
      [1] 46453
      idic
      katra
      logic
      [1]+  Done                    foo
      

      这里您的进程 ID 可能与我的不同,但您可以看到 logic 在其他 2 个函数的输出之后最后打印。

      【讨论】:

      • 我不关心输出的顺序;只是 foo 在后台运行,而不是分叉整个脚本。
      • 不,我并没有强调顺序,我只是指出函数 foo 确实在后台调用,barbaz 被正常调用,这就是输出顺序改变的原因.
      • 我知道foo 在后台运行,但我想确保子进程 运行foo,而不是继续其余的脚本(导致脚本的其余部分运行两次)
      猜你喜欢
      • 2021-08-28
      • 2020-12-06
      • 2011-06-19
      • 2019-01-23
      • 2018-12-05
      • 1970-01-01
      • 2019-06-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多