等待“任何过程”完成
在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_name
replace为出现在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_id
replace为程序的进程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