后台进程混淆了 shell 脚本的执行顺序

后台进程混淆了 shell 脚本的执行顺序

有人可以解释一下为什么这个脚本

echo one && 
echo two & 
echo three && 
echo four

给我

[1] 3692
three
four
one
two
[1]+  Done                    echo one && echo two

显然,后台进程后面的所有内容都会首先输出,然后按时间顺序打印后台进程之前的代码行。我在尝试编写的脚本中偶然发现了它,但不明白为什么它会这样(尽管我猜想它背后有一些独创性)。

我已经发现我可以用括号来防止这种情况:

echo one && 
(echo two &) 
echo three && 
echo four

给出

one
two
three
four

echo one && 
(sleep 2 && echo two &)
echo three && 
echo four

给出

one
three
four
two

因为第二行在后台休眠,因此在第一行已经执行时输出。

但为什么括号会有这样的效果呢?

附加问题:为什么括号会阻止后台 PID 的输出?

答案1

echo one && 
echo two & 
echo three && 
echo four

请记住,这被解析为

echo one && echo two &
echo three && echo four

我假设这些echo命令是成功的(它们只会在边缘情况下失败,例如重定向到完整磁盘上的文件)。因此,此代码片段并行运行两个任务:一个打印行onetwo,另一个打印行threefouronetwothree和 的散布four是不保证的,并且可能会因系统、shell 和调用的不同而有所不同。对于像这样的玩具示例,您可能会在轻负载系统上观察到可重现的结果,但这不是您可以指望的。

echo one && 
(echo two &) 
echo three && 
echo four

您已经更改了解析:这里只有命令echo two在后台执行。这保证了将one首先输出,并且像 before 一样three将始终在 before four,但 的位置two可以在 after 的任何位置one

如果后台进程是从主 shell 进程启动的,则 shell 仅打印有关后台进程的消息,而当它们在子 shell 中启动时则不会。括号创建一个子 shell,这就是为什么(echo two &)不会导致 shell 打印任何消息的原因。这会创建一个后台进程,但不是后台工作

echo one && 
(sleep 2 && echo two &)
echo three && 
echo four

在此代码片段中,后台作业在输出之前休眠 2 秒two。这使得极有可能(但实际上不能保证)将在和two后输出。threefour

答案2

第1部分

echo one && 
echo two & 
echo three && 
echo four

这可以重写为

echo one && echo two & 
echo three && echo four

首先得到threeand 的原因four很简单,就是让子 shell 处理oneandtwo启动并运行比打印threeand需要更长的时间four

你可以像这样更清楚地看到这一点

echo one && echo two &
sleep 1
echo three && echo four

在这种情况下,您会得到oneand two,一秒钟后会得到threeand four

请注意,在原始场景中,不能保证oneand不会与andtwo的输出混合,甚至可以想象,即使插入了单词,例如or 。threefourthonreeetwfouro

第2部分

echo one && 
(echo two &) 
echo three && 
echo four

这可以重写为

echo one && (echo two &) 
echo three && echo four

这里发生的是one立即打印 ,然后two在子 shell 中触发 。然后(串行)执行下一行,产生threefour。在您的特定情况下,子 shell 足够小且速度足够快,可以在输出two之前打印。three但也不能保证这一点。

第三部分

括号将语句组合到一个子 shell 中。 & 符号&适用于语句,但在本例中,您已用于&&将两个命令链接在一起,因此必须将它们视为单个语句。

也许你应该使用echo one; echo two而不是echo one && echo two?他们非常不同。分号;分隔两个命令,然后这两个命令独立但顺序运行。双 & 符号&&将两个命令连接在一起逻辑与,这样只有第一个成功完成后,第二个才会运行。比较false; echo yesfalse && echo yes、 和true && echo yes。然后通过替换&&(逻辑与) 和||逻辑或)。

奖金

您会丢失作业控制通知,因为子 shell 没有作业控制。

相关内容