174

Bash 中是否有任何内置功能可以等待进程完成?

wait命令只允许等待子进程完成。我想知道在继续执行任何脚本之前是否有任何方法可以等待任何进程完成。

执行此操作的机械方法如下,但我想知道 Bash 中是否有任何内置功能。

while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done
4

14 回答 14

202

等待任何进程完成

Linux(不适用于 ash 不支持的 Alpine tail --pid):

tail --pid=$pid -f /dev/null

达尔文(要求$pid有打开的文件):

lsof -p $pid +r 1 &>/dev/null

超时(秒)

Linux:

timeout $timeout tail --pid=$pid -f /dev/null

达尔文(要求$pid有打开的文件):

lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)
于 2017-01-12T12:41:49.470 回答
91

没有内置的。在循环中使用kill -0一个可行的解决方案:

anywait(){

    for pid in "$@"; do
        while kill -0 "$pid"; do
            sleep 0.5
        done
    done
}

或者作为更简单的 oneliner 以便一次性使用:

while kill -0 PIDS 2> /dev/null; do sleep 1; done;

正如几位评论员所指出的,如果您想等待您无权向其发送信号的进程,您可以找到其他方法来检测该进程是否正在运行以替换kill -0 $pid调用。在 Linux 上,test -d "/proc/$pid"可以在其他系统上使用pgrep(如果可用)或类似ps | grep "^$pid ".

于 2009-07-14T20:06:23.640 回答
55

我发现如果进程由 root (或其他)拥有,“kill -0”不起作用,所以我使用 pgrep 并想出了:

while pgrep -u root process_name > /dev/null; do sleep 1; done

这将具有可能匹配僵尸进程的缺点。

于 2012-05-02T04:52:19.320 回答
32

如果进程不存在,或者它是僵尸进程,则此 bash 脚本循环结束。

PID=<pid to watch>
while s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do
    sleep 1
done

编辑:上面的脚本 由Rockallite下面给出。谢谢!

我下面的原始答案适用于 Linux,依赖于procfsie /proc/。我不知道它的便携性:

while [[ ( -d /proc/$PID ) && ( -z `grep zombie /proc/$PID/status` ) ]]; do
    sleep 1
done

它不仅限于 shell,而且操作系统本身没有系统调用来监视非子进程终止。

于 2012-07-30T10:48:14.777 回答
15

FreeBSD 和 Solaris 有这个方便的pwait(1)实用程序,它完全可以满足您的需求。

我相信,其他现代操作系统也具有必要的系统调用(例如,MacOS 实现了 BSD kqueue),但并非所有操作系统都可以从命令行使用它。

于 2015-01-21T16:00:44.627 回答
11

从 bash 手册页

   wait [n ...]
          Wait for each specified process and return its termination  status
          Each  n  may be a process ID or a job specification; if a
          job spec is given, all processes  in  that  job's  pipeline  are
          waited  for.  If n is not given, all currently active child processes
          are waited for, and the return  status  is  zero.   If  n
          specifies  a  non-existent  process or job, the return status is
          127.  Otherwise, the return status is the  exit  status  of  the
          last process or job waited for.
于 2011-08-26T20:08:06.157 回答
6

所有这些解决方案都在 Ubuntu 14.04 中进行了测试:

解决方案 1(通过使用 ps 命令): 只是为了增加 Pierz 的答案,我建议:

while ps axg | grep -vw grep | grep -w process_name > /dev/null; do sleep 1; done

在这种情况下,grep -vw grep确保 grep 仅匹配 process_name 而不是 grep 本身。它的优点是支持 process_name 不在行尾的情况ps axg

解决方案 2(通过使用 top 命令和进程名称):

while [[ $(awk '$12=="process_name" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done

替换process_name为 中出现的进程名称top -n 1 -b。请保留引号。

要查看等待它们完成的进程列表,可以运行:

while : ; do p=$(awk '$12=="process_name" {print $0}' <(top -n 1 -b)); [[ $b ]] || break; echo $p; sleep 1; done

解决方案 3(通过使用 top 命令和进程 ID):

while [[ $(awk '$1=="process_id" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done

替换process_id为程序的进程 ID。

于 2015-04-23T11:26:04.557 回答
5

好的,看来答案是——不,没有内置工具。

设置/proc/sys/kernel/yama/ptrace_scope为后0,即可使用该strace程序。可以使用进一步的开关使其静音,以便它真正被动地等待:

strace -qqe '' -p <PID>
于 2014-04-06T11:34:52.650 回答
3

没有等待任何进程完成的内置功能。

您可以发送kill -0到找到的任何 PID,这样您就不会被僵尸和仍然可见的东西弄糊涂ps(同时仍然使用 检索 PID 列表ps)。

于 2009-06-29T17:19:03.050 回答
3

阻塞解决方案

在循环中使用wait,等待终止所有进程:

function anywait()
{

    for pid in "$@"
    do
        wait $pid
        echo "Process $pid terminated"
    done
    echo 'All processes terminated'
}

当所有进程终止时,此函数将立即退出。这是最有效的解决方案。

非阻塞解决方案

在循环中使用kill -0,等待终止所有进程+在检查之间做任何事情:

function anywait_w_status()
{
    for pid in "$@"
    do
        while kill -0 "$pid"
        do
            echo "Process $pid still running..."
            sleep 1
        done
    done
    echo 'All processes terminated'
}

反应时间减少到sleep时间,因为必须防止高 CPU 使用率。

一个实际的用法:

等待终止所有进程 + 通知用户所有正在运行的 PID。

function anywait_w_status2()
{
    while true
    do
        alive_pids=()
        for pid in "$@"
        do
            kill -0 "$pid" 2>/dev/null \
                && alive_pids+="$pid "
        done

        if [ ${#alive_pids[@]} -eq 0 ]
        then
            break
        fi

        echo "Process(es) still running... ${alive_pids[@]}"
        sleep 1
    done
    echo 'All processes terminated'
}

笔记

这些函数通过$@BASH 数组的参数获取 PID。

于 2015-09-08T04:28:07.650 回答
2

有同样的问题,我解决了杀死进程然后等待每个进程完成使用 PROC 文件系统的问题:

while [ -e /proc/${pid} ]; do sleep 0.1; done
于 2017-06-14T11:03:24.700 回答
1

当您的进程终止时,使用 inotifywait 监视某些关闭的文件。示例(在 Linux 上):

yourproc >logfile.log & disown
inotifywait -q -e close logfile.log

-e 指定要等待的事件,-q 表示仅在终止时输出最小。在这种情况下,它将是:

logfile.log CLOSE_WRITE,CLOSE

单个等待命令可用于等待多个进程:

yourproc1 >logfile1.log & disown
yourproc2 >logfile2.log & disown
yourproc3 >logfile3.log & disown
inotifywait -q -e close logfile1.log logfile2.log logfile3.log

inotifywait 的输出字符串会告诉你,哪个进程终止了。这仅适用于“真实”文件,不适用于 /proc/ 中的某些内容

于 2019-12-28T19:51:58.040 回答
0

在像 OSX 这样的系统上,您可能没有 pgrep,因此您可以在按名称查找进程时尝试这种方法:

while ps axg | grep process_name$ > /dev/null; do sleep 1; done

$进程名称末尾的符号确保 grep 仅将 process_name 匹配到 ps 输出中的行尾,而不是它本身。

于 2015-02-03T13:48:18.803 回答
0

Rauno Palosaari 的 , 的解决方案对于Timeout in Seconds Darwin没有 GNU 的类 UNIX 操作系统来说是一个很好的解决方法tail(它不是特定于Darwin)。但是,根据类 UNIX 操作系统的年龄,提供的命令行比必要的复杂,并且可能会失败:

lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)

在至少一个旧 UNIX 上,lsof参数+r 1m%s失败(即使对于超级用户):

lsof: can't read kernel name list.

m%s是一个输出格式规范。更简单的后处理器不需要它。例如,以下命令在 PID 5959 上最多等待 5 秒:

lsof -p 5959 +r 1 | awk '/^=/ { if (T++ >= 5) { exit 1 } }'

在此示例中,如果 PID 5959 在五秒内自行退出,${?}则为0。如果五秒后没有${?}返回。1

可能值得注意的是,在 中+r 11是轮询间隔(以秒为单位),因此可以根据情况进行更改。

于 2020-04-17T18:51:59.233 回答