我正在用shell脚本调试一个奇怪的东西,想知道我是否误解了Bash的"fork"(&
)的工作原理,也许是因为我通常通过终端在单个命令中使用它,例如:
[~me]$ someExecutable &
现在,在一个shell脚本中:
foo() {
echo "logic"
}
bar() {
echo "idic"
}
baz() {
echo "katra"
}
foo &
bar
baz
我的意图是异步调用函数foo
,然后让脚本的其余部分继续在原始流程上下文中执行。
当我执行这个示例脚本时,我会得到预期的输出:
logic
idic
katra
(也就是说,只有对bar
和baz
的单个明显调用;我一点也不为三行输出的相对顺序所困扰,我知道这可能会有所不同)
但我可能误解了输出和脚本本身。如果是这样的话,这肯定可以解释我在真实代码中看到的奇怪之处,并使我不必再深入研究了。
在子进程中,在调用foo
之后,分叉的shell脚本是否可能继续?这意味着我的shell脚本的剩余部分将执行两次?
当我在Bash脚本中像这样使用&
时,会发生什么
在您的示例中,"与"符号只是派生对foo()的调用。它并没有分叉整个脚本。只有foo()在一个单独的线程中运行,当foo(()完成时就会退出。bar()和baz()继续在同一线程中运行
bash中的&
不是一个fork。它告诉bash在子shell中启动命令,并继续解析其他命令(而不是等待该命令完成)。
因此,正如您所期望的,它在后台运行"foo",并在顶级shell中继续处理"bar"one_answers"baz"命令。调用shell没有分叉,但"foo"是在后台运行的。
因此,"foo"的输出实际上可能发生在bar或baz之后,这取决于它的内容。。但由于它使用了"echo",这是一个shell内置程序,当"foo"后台进程设置好时,"echo"立即可用:因此,在调用shell传递到下一个命令之前,很有可能看到它的输出,这显然就是您的情况。(但是YMMV!如果"foo"调用了一个繁重的外部命令,那么它的输出很可能会在稍后发生)
在终端中,这几乎是一样的:你可以考虑一个带有shell提示的终端窗口作为该shell的文件脚本,登录shell正在读取"当你键入它时"。行为几乎相同。
&
字符告诉shell在后台执行一个命令(或一组命令)。
接下来的命令将立即执行。
执行两次脚本没有危险,因此它在这方面与C的fork()
不同。
除了控制运算符&
导致命令在子shell中异步执行1之外,您还可以使用协处理器来控制输出。
而不是通过说:来调用函数
foo &
比如:
coproc fubar { foo; }
然后通过说:读取输出
cat <&"${fubar[0]}"
这也将允许您控制输出(无论它是否需要在bar
或baz
等其他函数之后或之前发出)。
例如:
foo() {
echo "logic1"
echo "logic2"
}
bar() {
echo "idic"
}
baz() {
echo "katra"
}
coproc fubar { foo; }
bar
baz
cat <&"${fubar[0]}"
将产生:
idic
katra
logic1
logic2
实际上,这个命令确实在后台运行您的函数:
foo &
但是当您输入bar
和baz
时,foo已经完成了它的运行并在您的终端上打印了它的输出。
然而,如果你这样运行你的功能,那么你会注意到区别:
foo & bar; baz
[1] 46453
idic
katra
logic
[1]+ Done foo
在这里,您的进程id可能与我的不同,但您可以看到logic
最终是在其他2个函数的输出之后打印的。