脚本中无法循环执行直至后台进程完成(使用‘jobs’)

脚本中无法循环执行直至后台进程完成(使用‘jobs’)

我尝试循环执行,直到后台进程(在脚本中较早启动)完成。一个易于重现的测试用例是:

ping -c 10 localhost &>/dev/null &

在命令行上,我可以循环while [[ -n $(jobs) ]](当$(jobs)不为空时)。

$ ping -c 10 localhost &>/dev/null &
[1] 19078
$ while [[ -n $(jobs) ]]; do echo -n .; sleep 1; done
.........[1]+  Done                    ping -c 5 localhost &> /dev/null

但是,脚本中的相同两行将继续打印.s 直到我点击Ctrl-C

奇怪的是,如果我jobs在循环内调用,脚本就会按预期结束。

$ ./background-ping.sh
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Done                    ping -c 5 localhost &> /dev/null

我知道还有其他方法可以检查后台作业是否完成(例如检查/proc),但我想知道为什么检查jobs不能按预期进行。

答案1

如果我理解了你的问题,你的两行代码就不会在脚本中停止。

这就是我做的:

ping -c 5 google.com &>/dev/null &
while [[ -n $(jobs -r) ]]; do echo -n "."; sleep 1; done

jobs -r检查正在运行的进程,并且调用我的脚本按预期工作,当 ping 完成时脚本停止。

编辑:我认为在脚本中,父进程被称为正在运行的进程,这就是为什么jobs继续认为有一个正在运行的进程。这是一个假设

这就是答案吗?(或者也许我真的不明白你的意思,我的英语不好可能是问题所在......)

答案2

无论何时遇到此类问题,您都应该尝试打印变量,以便了解发生了什么。在这种情况下,jobs还会返回已完成的作业。如果您运行此命令:

ping -c 5 localhost &>/dev/null &
while [[ -n $(jobs | tee -a temp) ]]; do
    echo -n .; 
    sleep 1; 
done

您将在 中看到以下输出temp

[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[...]

所以,这里的输出jobs永远不会为空。即使作业完成,jobs仍然会返回Done消息。这就是 Metal3d 的解决方案奏效的原因jobs -r

更令人困惑的是为什么在循环内运行会导致它正常工作。答案可能与在单独的子 shell 中运行jobs有关,但我不确定细节。我有while [[ -n $(jobs) ]]jobs发了一个问题如果有人感兴趣的话,可以在 U&L 上查看这个内容。

答案3

您可以使用wait等待后台作业停止的命令

例子:

$ ping -c 10 localhost &>/dev/null &
[29787]
$ wait
[1]+  Done ping -c 10 localhost &>/dev/null

等待命令阻塞,当ping完成时,提示被释放然后打印一条消息。

您可以将 is 与多个命令一起使用:

$ ping -c 5 localhost &>/dev/null & ping -c 5 facebook.com  &>/dev/null &
[1] 29867
[2] 29868
$ wait
[1]-  Done                    ping -c 5 localhost &>/dev/null
[2]+  Done                    ping -c 5 facebook.com &>/dev/null

相关内容