繁体   English   中英

等待一个进程完成

[英]Wait for a process to finish

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

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

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

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

等待任何进程完成

Linux:

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)

没有内置。 在循环中使用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 "东西ps | grep "^$pid " ps | grep "^$pid "

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

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

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

如果进程不存在,或者它是一个僵尸进程,那么这个 bash 脚本循环就会结束。

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

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

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

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

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

FreeBSD 和 Solaris 有这个方便的pwait(1)实用程序,它完全符合您的要求。

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

从 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.

所有这些解决方案都在 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的行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。

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

/proc/sys/kernel/yama/ptrace_scope0 ,就可以使用strace程序了。 可以使用进一步的开关使其静默,以便它真正被动地等待:

strace -qqe '' -p <PID>

阻塞解决方案

在循环中使用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。

我找到了一个等待任何PID的小工具,并在PID死亡时立即返回。

https://github.com/izabera/waiter

这是一个很小的C文件,你可以用gcc编译。

#define _GNU_SOURCE
#include <sys/ptrace.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
  if (argc == 1) return 1;
  pid_t pid = atoi(argv[1]);
  if (ptrace(PTRACE_SEIZE, pid, NULL, NULL) == -1) return 1;
  siginfo_t sig;
  return waitid(P_PID, pid, &sig, WEXITED|WNOWAIT);
}

它的工作方式与调试器附加到进程时的工作方式相同。

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

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

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

while [ -e /proc/${pid} ]; do sleep 0.1; done

当您的进程终止时,使用 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/

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

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

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

Rauno Palosaari 的Timeout in Seconds Darwin解决方案,对于没有 GNU tail的类 UNIX 操作系统是一个很好的解决方法(它不是特定于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 上等待最多五秒钟:

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

在此示例中,如果 PID 5959 在 5 秒过去之前自行退出,则${?}0 如果不是${?}在五秒后返回1

可能值得注意的是,在+r 11是轮询间隔(以秒为单位),因此可能会更改以适应情况。

Fedora/Cent Os 使用 yum install 时同样的问题,用 Ctrl+Z 中断它。

使用这个命令:

sudo cat /var/cache/dnf/*pid

如果有任何pid,您可以手动删除它:

sudo nano /var/cache/dnf/*pid

crtl+x 后跟 Y 和 Enter 退出 nano 编辑器(您可以使用任何要打开的编辑器)。

如果之后,删除一些可能仍然存在的 tmp 文件:

sudo systemd-tmpfiles --remove dnf.conf

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM