等待“任何过程”完成

在bash中是否有任何内置function来等待任何进程完成? wait命令只允许等待subprocess完成。 我想知道在进行任何脚本之前是否有任何方法等待任何过程完成。

一个机械的方法来做到这一点如下,但我想知道是否有任何内置function在bash中。

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

没有内build。 在循环中使用kill -0来获得可行的解决scheme:

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

或者作为一个简单的一次性使用方法,

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

正如几位评论员所指出的那样,如果您想等待您没有发送信号的特权的进程,则可以通过其他方式来检测进程是否正在运行以replacekill -0 $pid调用。 在Linux上, test -d "/proc/$pid"可以工作,在其他系统上,你可能必须使用pgrep (如果有的话)或类似ps | grep ^$pid ps | grep ^$pid

我发现“杀-0”不工作,如果进程是由根(或其他)拥有,所以我用pgrep和想出了:

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

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

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

 PID=<pid to watch> while s=`ps -p $PID -os=` && [[ "$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,但是操作系统本身没有系统调用来监视非subprocess终止。

等待任何过程完成

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) 

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

FreeBSD和Solaris有这个方便的pwait(1)工具,它可以完全按照你的需要来做。

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

所有这些解决scheme都在Ubuntu 14.04中进行了testing:

解决scheme1(通过使用ps命令):只要加起来Pierz答案,我会build议:

 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 axgps axg

解决scheme2(使用顶级命令和进程名称):

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

process_namereplace为出现在top -n 1 -b的进程名称。 请保留引号。

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

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

解决scheme3(通过使用顶级命令和进程ID):

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

process_idreplace为程序的进程ID。

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

/proc/sys/kernel/yama/ptrace_scope0 ,可以使用strace程序。 更多的开关可以用来使它保持沉默,所以它确实被动地等待:

 strace -qqe '' -p <PID> 

没有内build的function来等待任何进程完成。

你可以把kill -0发送给任何发现的PID,所以你不会被僵尸和ps中仍然可见的东西困惑(同时仍然使用ps检索PID列表)。

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

https://github.com/izabera/waiter

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

 #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); } 

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

在像OSX这样的系统上,你可能没有使用pgrep,所以你可以在按名称查找进程时试试这个方法:

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

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

阻止解决scheme

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

 function anywait() { for pid in "$@" do wait $pid echo "Process $pid terminated" done echo 'All processes terminated' } 

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

非阻塞解决scheme

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

反应时间减less到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。

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

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