等待进程完成

发布于 2024-07-26 03:14:45 字数 243 浏览 5 评论 0原文

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

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

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

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

Is there any builtin feature in Bash to wait for a process to finish?

The wait command only allows one to wait for child processes to finish.
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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

寂寞陪衬 2024-08-02 03:14:46

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

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

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

On a system like OSX you might not have pgrep so you can try this appraoch, when looking for processes by name:

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.

小霸王臭丫头 2024-08-02 03:14:45

等待任何进程完成

Linux(在 Alpine 上不起作用,其中 ash 不支持 tail --pid):

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

Darwin(要求 $pid 已打开文件):

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

超时(秒)

Linux:

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

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

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

To wait for any process to finish

Linux (doesn't work on Alpine, where ash doesn't support tail --pid):

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

Darwin (requires that $pid has open files):

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

With timeout (seconds)

Linux:

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

Darwin (requires that $pid has open files):

lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)
梦醒灬来后我 2024-08-02 03:14:45

没有内置的。 在循环中使用 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”

There's no builtin. Use kill -0 in a loop for a workable solution:

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:

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. On Linux, test -d "/proc/$pid" works, on other systems you might have to use pgrep (if available) or something like ps | grep "^$pid ".

意犹 2024-08-02 03:14:45

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

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

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

I found "kill -0" does not work if the process is owned by root (or other), so I used pgrep and came up with:

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

This would have the disadvantage of probably matching zombie processes.

吃→可爱长大的 2024-08-02 03:14:45

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

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

编辑:上面的脚本给出如下< /a> 作者:Rockallite。 谢谢!

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

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

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

This bash script loop ends if the process does not exist, or it's a zombie.

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. Thanks!

My orignal answer below works for Linux, relying on procfs i.e. /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.

青春如此纠结 2024-08-02 03:14:45

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

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

FreeBSD and Solaris have this handy pwait(1) utility, which does exactly, what you want.

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.

蓬勃野心 2024-08-02 03:14:45

来自 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.

From the bash manpage

   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.
本王不退位尔等都是臣 2024-08-02 03:14:45

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

All these solutions are tested in Ubuntu 14.04:

Solution 1 (by using ps command):
Just to add up to Pierz answer, I would suggest:

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. It has the advantage of supporting the cases where the process_name is not at the end of a line at ps axg.

Solution 2 (by using top command and process name):

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. 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):

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.

清晨说晚安 2024-08-02 03:14:45

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

/proc/sys/kernel/yama/ptrace_scope设置为0后,就可以使用strace程序了。 可以使用更多开关使其保持沉默,以便它真正被动等待:

strace -qqe '' -p <PID>

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. Further switches can be used to make it silent, so that it really waits passively:

strace -qqe '' -p <PID>
慕烟庭风 2024-08-02 03:14:45

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

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

Had the same issue, I solved the issue killing the process and then waiting for each process to finish using the PROC filesystem:

while [ -e /proc/${pid} ]; do sleep 0.1; done
眼前雾蒙蒙 2024-08-02 03:14:45

阻塞解决方案

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

反应时间减少到睡眠时间,因为必须防止 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。

Blocking solution

Use the wait in a loop, for waiting for terminate all processes:

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:

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.

A realistic usage:

Waiting for terminate all processes + inform user about all running PIDs.

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.

深海不蓝 2024-08-02 03:14:45

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

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

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

九八野马 2024-08-02 03:14:45

如果您需要终止进程并等待其完成,可以使用 killall(1)(基于进程名称)和 start-stop-daemon(8)(基于 pidfile)。

终止与 someproc 匹配的所有进程并等待它们终止:(

killall someproc --wait             # wait forever until matching processes die
timeout 10s killall someproc --wait # timeout after 10 seconds

不幸的是,对于特定的特定进程,没有 --waitkill 的直接等效项PID)。

使用信号 SIGINT 终止基于 pidfile /var/run/someproc.pid 的进程,同时使用 SIGKILL 等待它完成20 秒超时后发送,使用:

start-stop-daemon --stop --signal INT --retry 20 --pidfile /var/run/someproc.pid

If you need to both kill a process and wait for it finish, this can be achieved with killall(1) (based on process names), and start-stop-daemon(8) (based on a pidfile).

To kill all processes matching someproc and wait for them to die:

killall someproc --wait             # wait forever until matching processes die
timeout 10s killall someproc --wait # timeout after 10 seconds

(Unfortunately, there's no direct equivalent of --wait with kill for a specific pid).

To kill a process based on a pidfile /var/run/someproc.pid using signal SIGINT, while waiting for it to finish, with SIGKILL being sent after 20 seconds of timeout, use:

start-stop-daemon --stop --signal INT --retry 20 --pidfile /var/run/someproc.pid
泡沫很甜 2024-08-02 03:14:45

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

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

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

logfile.log CLOSE_WRITE,CLOSE

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

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

inotifywait 的输出字符串将告诉您哪个进程终止。 这只适用于“真实”文件,不适用于 /proc/ 中的内容

Use inotifywait to monitor some file that gets closed, when your process terminates. Example (on 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. 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. This only works with 'real' files, not with something in /proc/

温柔戏命师 2024-08-02 03:14:45

Rauno Palosaari 的 Timeout in Seconds Darwin 解决方案对于没有 GNU tail 的类 UNIX 操作系统来说是一个很好的解决方法(它不是具体到达尔文)。 但是,根据类 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 在五秒过去之前自行退出,则 ${?}0。 如果不是,${?} 将在五秒后返回 1

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

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). But, depending on the age of the UNIX-like operating system, the command-line offered is more complex than necessary, and can fail:

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):

lsof: can't read kernel name list.

The m%s is an output format specification. A simpler post-processor does not require it. For example, the following command waits on PID 5959 for up to five seconds:

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. If not ${?} returns 1 after five seconds.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文