简体   繁体   English

等待一个进程完成

[英]Wait for a process to finish

Is there any builtin feature in Bash to wait for a process to finish? Bash 中是否有任何内置功能可以等待进程完成?

The wait command only allows one to wait for child processes to finish. wait命令只允许等待子进程完成。 I would like to know if there is any way to wait for any process to finish before proceeding in any script.我想知道在继续执行任何脚本之前是否有任何方法可以等待任何进程完成。

A mechanical way to do this is as follows but I would like to know if there is any builtin feature in Bash.执行此操作的机械方法如下,但我想知道 Bash 中是否有任何内置功能。

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

To wait for any process to finish等待任何进程完成

Linux: Linux:

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

Darwin (requires that $pid has open files):达尔文(要求$pid有打开的文件):

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

With timeout (seconds)超时(秒)

Linux: Linux:

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

Darwin (requires that $pid has open files):达尔文(要求$pid有打开的文件):

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

There's no builtin.没有内置。 Use kill -0 in a loop for a workable solution:在循环中使用kill -0以获得可行的解决方案:

anywait(){

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

Or as a simpler oneliner for easy one time usage:或者作为一个更简单的 oneliner,方便一次性使用:

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

As noted by several commentators, if you want to wait for processes that you do not have the privilege to send signals to, you have find some other way to detect if the process is running to replace the kill -0 $pid call.正如几位评论员所指出的,如果您想等待您没有权限向其发送信号的进程,您可以找到其他方法来检测进程是否正在运行以替换kill -0 $pid调用。 On Linux, test -d "/proc/$pid" works, on other systems you might have to use pgrep (if available) or something like ps | grep "^$pid "在 Linux 上, test -d "/proc/$pid"有效,在其他系统上,您可能必须使用pgrep (如果可用)或ps | grep "^$pid "东西ps | grep "^$pid " ps | grep "^$pid " . ps | grep "^$pid "

I found "kill -0" does not work if the process is owned by root (or other), so I used pgrep and came up with:我发现如果进程由 root(或其他人)拥有,“kill -0”不起作用,所以我使用 pgrep 并想出了:

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

This would have the disadvantage of probably matching zombie processes.这将具有可能匹配僵尸进程的缺点。

This bash script loop ends if the process does not exist, or it's a zombie.如果进程不存在,或者它是一个僵尸进程,那么这个 bash 脚本循环就会结束。

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

EDIT : The above script was given below by Rockallite .编辑:上面的脚本Rockallite 在下面给出的。 Thanks!谢谢!

My orignal answer below works for Linux, relying on procfs ie /proc/ .我下面的原始答案适用于 Linux,依赖于procfs/proc/ I don't know its portability:我不知道它的便携性:

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

It's not limited to shell, but OS's themselves do not have system calls to watch non-child process termination.它不仅限于 shell,而且操作系统本身没有系统调用来观察非子进程终止。

FreeBSD and Solaris have this handy pwait(1) utility, which does exactly, what you want. FreeBSD 和 Solaris 有这个方便的pwait(1)实用程序,它完全符合您的要求。

I believe, other modern OSes also have the necessary system calls too (MacOS, for example, implements BSD's kqueue ), but not all make it available from command-line.我相信,其他现代操作系统也有必要的系统调用(例如,MacOS 实现了 BSD 的kqueue ),但并非所有操作系统都可以从命令行使用它。

From the bash manpage从 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.

All these solutions are tested in Ubuntu 14.04:所有这些解决方案都在 Ubuntu 14.04 中进行了测试:

Solution 1 (by using ps command): Just to add up to Pierz answer, I would suggest:解决方案 1(通过使用 ps 命令):为了补充 Pierz 的答案,我建议:

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

In this case, grep -vw grep ensures that grep matches only process_name and not grep itself.在这种情况下, grep -vw grep确保 grep 仅匹配 process_name 而不是 grep 本身。 It has the advantage of supporting the cases where the process_name is not at the end of a line at ps axg .它的优点是支持 process_name 不在ps axg的行ps axg

Solution 2 (by using top command and process name):解决方案 2(通过使用 top 命令和进程名称):

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

Replace process_name with the process name that appears in top -n 1 -b .process_name替换为top -n 1 -b中出现的进程名称。 Please keep the quotation marks.请保留引号。

To see the list of processes that you wait for them to be finished, you can run:要查看等待它们完成的进程列表,您可以运行:

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

Solution 3 (by using top command and process ID):解决方案 3(通过使用 top 命令和进程 ID):

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

Replace process_id with the process ID of your program.process_id替换为您程序的进程 ID。

Okay, so it seems the answer is -- no, there is no built in tool.好吧,看来答案是——不,没有内置工具。

After setting /proc/sys/kernel/yama/ptrace_scope to 0 , it is possible to use the strace program./proc/sys/kernel/yama/ptrace_scope0 ,就可以使用strace程序了。 Further switches can be used to make it silent, so that it really waits passively:可以使用进一步的开关使其静默,以便它真正被动地等待:

strace -qqe '' -p <PID>

Blocking solution阻塞解决方案

Use the wait in a loop, for waiting for terminate all processes:在循环中使用wait ,用于等待终止所有进程:

function anywait()
{

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

This function will exits immediately, when all processes was terminated.当所有进程终止时,此函数将立即退出。 This is the most efficient solution.这是最有效的解决方案。

Non-blocking solution无阻塞解决方案

Use the kill -0 in a loop, for waiting for terminate all processes + do anything between checks:在循环中使用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'
}

The reaction time decreased to sleep time, because have to prevent high CPU usage.反应时间减少到sleep时间,因为必须防止高 CPU 使用率。

A realistic usage:实际用法:

Waiting for terminate all processes + inform user about all running PIDs.等待终止所有进程 + 通知用户所有正在运行的 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'
}

Notes笔记

These functions getting PIDs via arguments by $@ as BASH array.这些函数通过$@作为 BASH 数组的参数获取 PID。

I found a small utility that waits on any PID and returns immediately when the PID dies. 我找到了一个等待任何PID的小工具,并在PID死亡时立即返回。

https://github.com/izabera/waiter https://github.com/izabera/waiter

It's a tiny C file you can compile with gcc. 这是一个很小的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);
}

It works the same way a debugger would when it attaches to a process. 它的工作方式与调试器附加到进程时的工作方式相同。

There is no builtin feature to wait for any process to finish.没有等待任何进程完成的内置功能。

You could send kill -0 to any PID found, so you don't get puzzled by zombies and stuff that will still be visible in ps (while still retrieving the PID list using ps ).您可以将kill -0发送到找到的任何 PID,这样您就不会被僵尸和在ps仍然可见的东西所困扰(同时仍然使用ps检索PID列表)。

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

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

Use inotifywait to monitor some file that gets closed, when your process terminates.当您的进程终止时,使用 inotifywait 来监视某些关闭的文件。 Example (on Linux):示例(在 Linux 上):

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

-e specifies the event to wait for, -q means minimal output only on termination. -e 指定要等待的事件,-q 表示仅在终止时输出最少。 In this case it will be:在这种情况下,它将是:

logfile.log CLOSE_WRITE,CLOSE

A single wait command can be used to wait for multiple processes:单个等待命令可用于等待多个进程:

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

The output string of inotifywait will tell you, which process terminated. inotifywait 的输出字符串会告诉你,哪个进程终止了。 This only works with 'real' files, not with something in /proc/这仅适用于“真实”文件,不适用于 /proc/

On a system like OSX you might not have pgrep so you can try this appraoch, when looking for processes by name:在像 OSX 这样的系统上,您可能没有 pgrep,因此您可以在按名称查找进程时尝试这种方法:

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

The $ symbol at the end of the process name ensures that grep matches only process_name to the end of line in the ps output and not itself.进程名称末尾的$符号确保 grep 仅将 process_name 匹配到 ps 输出中的行尾,而不是匹配它本身。

Rauno Palosaari's solution for Timeout in Seconds Darwin , is an excellent workaround for a UNIX-like OS that does not have GNU tail (it is not specific to Darwin ). Rauno Palosaari 的Timeout in Seconds Darwin解决方案,对于没有 GNU tail的类 UNIX 操作系统是一个很好的解决方法(它不是特定于Darwin )。 But, depending on the age of the UNIX-like operating system, the command-line offered is more complex than necessary, and can fail:但是,根据类 UNIX 操作系统的年龄,提供的命令行比必要的复杂,并且可能会失败:

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

On at least one old UNIX, the lsof argument +r 1m%s fails (even for a superuser):在至少一个旧的 UNIX 上, lsof参数+r 1m%s失败(即使对于超级用户):

lsof: can't read kernel name list.

The m%s is an output format specification. m%s是输出格式规范。 A simpler post-processor does not require it.更简单的后处理器不需要它。 For example, the following command waits on PID 5959 for up to five seconds:例如,以下命令在 PID 5959 上等待最多五秒钟:

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

In this example, if PID 5959 exits of its own accord before the five seconds elapses, ${?} is 0 .在此示例中,如果 PID 5959 在 5 秒过去之前自行退出,则${?}0 If not ${?} returns 1 after five seconds.如果不是${?}在五秒后返回1

It may be worth expressly noting that in +r 1 , the 1 is the poll interval (in seconds), so it may be changed to suit the situation.可能值得注意的是,在+r 11是轮询间隔(以秒为单位),因此可能会更改以适应情况。

Fedora/Cent Os same trouble when used yum install and interrupt it with Ctrl+Z. Fedora/Cent Os 使用 yum install 时同样的问题,用 Ctrl+Z 中断它。

Use this command:使用这个命令:

sudo cat /var/cache/dnf/*pid

if there any pid, you can manually remove it:如果有任何pid,您可以手动删除它:

sudo nano /var/cache/dnf/*pid

crtl+x followed by Y and Enter to exit nano editor (you can use any editor you want to open). crtl+x 后跟 Y 和 Enter 退出 nano 编辑器(您可以使用任何要打开的编辑器)。

If after, delete some tmp files that may still be there:如果之后,删除一些可能仍然存在的 tmp 文件:

sudo systemd-tmpfiles --remove dnf.conf

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

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