等待“任何过程”完成
在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 axg行ps 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_scope为0 ,可以使用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