如何检查后台作业是不是存在? (重击)

Posted

技术标签:

【中文标题】如何检查后台作业是不是存在? (重击)【英文标题】:How to check whether a background job is alive? (bash)如何检查后台作业是否存在? (重击) 【发布时间】:2012-06-29 16:01:24 【问题描述】:

我有以下 bash 脚本,我们可以称之为script1.sh

#!/bin/bash

exec ./script2.sh &

sleep 5

if job1 is alive then #<--- this line is pseudo-code!
    exec ./script3.sh &
    wait
fi

可以看出,脚本将script2.sh 作为后台作业执行,然后等待5 秒(因此script2.sh 可以做一些初始化工作)。如果初始化成功,script2.sh这个job还是会存活的,这种情况下我也想同时启动script3.sh;如果没有,我只想退出。

但是,我不知道如何检查第一个作业是否还活着,因此是伪代码行。那么,应该用什么来代替它呢?

【问题讨论】:

运行 exec 不会像你想的那样。它将用新进程替换当前进程;将无法到达脚本中的后面几行。 【参考方案1】:

您可以使用$! 获取最近的后台作业的 PID。然后,您可以检查 ps 的退出状态以确定该特定 PID 是否仍在进程列表中。例如:

sleep 30 &
if ps -p $! >&-; then
    wait $!
else
    jobs
fi

【讨论】:

那个确切的命令给了我错误 ps: write error: Bad Filedescriptor 这并不总是有效。例如,在 Alpine Linux 中,默认的“ps”绑定到 BusyBox,它不支持 -p 开关。使用像 jobs 这样的 bash 内置函数更便携。 我从&gt;&amp;- 得到一个“ps:写入错误:错误的文件描述符”,这似乎是因为M&gt;&amp;- 中没有数字文件描述符M关闭文件描述符M。我改用&gt;/dev/null 2&amp;&gt;1 或者检查pid是否存在于/proc dir exec ./script2.sh &amp;; pid=$!; [[ -e /proc/$pid ]] &amp;&amp; yor_code; @EltonCarvalho,谢谢,将其添加为答案。【参考方案2】:

您可以检查信号是否可交付

./script2 &
myPid=$!
sleep 5

if kill -0 "$myPid"; then
    script3 &
    wait
fi

【讨论】:

【参考方案3】:

要扩展 fduff 的答案,您可以使用 jobs 内置函数:

if jobs %%; then
    exec ./script3.sh &
    wait
fi

jobs %% 打印最近的后台进程(“当前作业”)的作业 ID,成功时返回 0,如果没有此类作业,则返回 1。

【讨论】:

Todd A. Jacobs 的方法在我的 Ubuntu 18.04 系统 (Bad file descriptor) 上失败并且 ormaaj 的工作,但 kill -0 可能会失败(即,非零返回码),原因有两个:你不'没有权限或 PID 没有运行。 AFAIK,jobs 是一个“更干净”(奥卡姆剃刀)的答案,因为(1)它可以工作,(2)“失败”不取决于您的权限,而是取决于 PID 未运行,从而减少了您的错误检查代码需要写。 如果您只需要检查一项后台作业,这可以正常工作。此外,如果您在带有 unshare 的 PID 命名空间中运行脚本,这也是一个简单的解决方案。在后一种情况下 $!将返回命名空间内的 PID 和 ps -p $!不会在 pid 列表中找到它。对于这种情况,我还没有(还)找到简单的替代方案。 @mellow-yellow 我们必须超越记录在案的错误。对于大多数用途,权限失败在逻辑上是不可能的。如果您将作业说明符而不是 PID 传递给kill,则您消除了由于 PID 重用而使自己进入另一个用户进程的竞争条件的可能性。我什至不知道您有任何方式无权向您自己的子进程发出信号。也就是说,我同意如果您想最大限度地正确,正确使用jobs(假设它对于用例来说足够便携)可能会更好。【参考方案4】:

我不确定 exec 是否适用于背景,因为它会将父进程的图像替换为子进程的图像,否则,如果我们假设您摆脱了 exec,您会想要类似的东西:

#!/bin/bash
./script2.sh&
pid1=$!

if kill -0 $pid1; then
  ./script3.sh&
  pid3=$!
  wait
fi

【讨论】:

您应该尽可能使用作业说明符而不是 PID 以获得更高的可靠性:kill -0 %1 用于第一个作业,kill -0 %% 用于最近生成的作业,等等。(关键限制是您必须知道作业编号或说明符是什么,在某些脚本中,在您启动后台作业的位置可能比 PID 更难知道或预测。) 如果它更难使用你为什么说我应该使用它? 答案就在我的第一句话中:“为了更高的可靠性”。一个更合适的问题是“它以什么方式提高可靠性?”。我也没有说它更难使用。我说过在某些情况下可能更难使用。但“为了更高的可靠性”的逻辑含义是,实际上更难正确地使用 PID,或者以能够获得与工作规范相同的可靠性的方式使用 PID。对于 PID,PID 重新分配存在竞争条件。不太可能,但我们必须考虑如果这样做会对我们的脚本或用户造成什么后果。 实际上,经过深思熟虑后,我想我想把我的主张退回一点:如果您在启动子进程和使用kill 检查 PID 之间从未调用过wait ,那么你应该是安全的。因为子进程(但不是孙子进程或除我们进程的直接子进程之外的任何其他进程等)的 PI​​D 不能被外部活动从我们下面重用,直到我们收集了它的退出状态带有wait【参考方案5】:

将您的后台进程 pid 复制到 var 中

./script2.sh &; pid=$!

然后检查/proc目录中是否存在这个pid

[[ -e /proc/$pid ]] &&  your_code; 

ls

ls /proc/$pid 2>/dev/null &&  your_code; 

【讨论】:

【参考方案6】:

看看jobs 命令;它列出了所有正在运行的作业。

【讨论】:

以上是关于如何检查后台作业是不是存在? (重击)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Sidekiq 运行连续的后台作业?

如何在本地运行 Parse 后台作业

用于重复后台作业的延迟作业、守护进程或其他 gem

如何在 AEM 作为云中创建后台作业?

HapiJS 启动更长的后台进程

Hive 命令行 如果它不是在后台映射减少作业,则选择查询时间不正确