Bash中是否有任何内置功能来等待进程完成?
该wait
命令仅允许一个人等待子进程完成。我想知道在执行任何脚本之前是否有任何方法可以等待任何进程完成。
一种机械的方法如下,但是我想知道Bash中是否有任何内置功能。
while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done
Bash中是否有任何内置功能来等待进程完成?
该wait
命令仅允许一个人等待子进程完成。我想知道在执行任何脚本之前是否有任何方法可以等待任何进程完成。
一种机械的方法如下,但是我想知道Bash中是否有任何内置功能。
while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done
Answers:
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)
tail
会做到这一点。
tail
通过轮询kill(pid, SIG_0)
一个进程(使用进行发现strace
)在后台进行工作。
+r 1
超时),我个人是在寻找不使用轮询的MacOS解决方案。
tail
有线kill (pid, 0) != 0 && errno != EPERM
。
caffeinate -w $pid
就可以解决问题。
没有内置的。使用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 "
。
sleep 0.5
,与的$pid
进程可能会死,并且可能使用该进程创建另一个进程$pid
。最后,我们将等待两个具有相同的不同过程(甚至更多)$pid
。
我发现如果进程由root(或其他)拥有,则“ kill -0”不起作用,因此我使用了pgrep并提出了:
while pgrep -u root process_name > /dev/null; do sleep 1; done
这将具有可能匹配僵尸进程的缺点。
kill(pid, sig=0)
如果调用者进程没有杀死特权,则系统调用将失败。因此,/ bin / kill -0和“ kill -0”(内置bash)在相同条件下也会失败。
如果该进程不存在或为僵尸,则此bash脚本循环结束。
PID=<pid to watch>
while s=`ps -p $PID -o s=` && [[ "$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
它不限于外壳,但OS本身没有系统调用来监视非子进程的终止。
grep /proc/$PID/status
用双引号(bash: test: argument expected
)包围
while s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do sleep 1; done
ps
,中都没有-p
或s=
不受支持
FreeBSD和Solaris有这个方便的pwait(1)
实用程序,它可以根据您的需要进行操作。
我相信,其他现代OS也具有必要的系统调用(例如,MacOS实现了BSD的调用kqueue
),但并非所有人都能从命令行使用它。
> BSD and Solaris
:检查想到的三大BSD;OpenBSD和NetBSD都不具有此功能(在其手册页中),只有FreeBSD具备此功能,因为您可以轻松地检查man.openbsd.org。
kqueue
,因此编译FreeBSD pwait(1)
并不容易。为什么其他BSD的导入功能无法让我逃脱...
plink me@oracle box -pw redacted "pwait 6998";email -b -s "It's done" etc
只是让我现在回家,而不是现在的几个小时。
从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.
wait
没有args会阻塞进程,直到任何子进程完成。老实说,我一直等待任何进程,因为总是有系统进程在进行。
sleep 1000
ctrl-z
wait [sleep pid]
立即返回
所有这些解决方案均已在Ubuntu 14.04中进行了测试:
解决方案1(通过使用ps命令): 总结一下皮尔斯的答案,我建议:
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(通过使用顶部命令和进程名称):
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(通过使用顶部命令和进程ID):
while [[ $(awk '$1=="process_id" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done
替换process_id
为您的程序的进程ID。
grep -v grep
管道是一个庞大的反模式,这以您没有相同名称的不相关进程为前提。如果您知道PID,则可以将其调整为正常工作的解决方案。
-w
以在grep -v grep
某种程度上避免该问题。我还根据您的评论添加了两个解决方案。
好的,看来答案是-不,没有内置工具。
设置/proc/sys/kernel/yama/ptrace_scope
为后0
,可以使用该strace
程序。可以使用其他开关使它静音,从而使其真正被动地等待:
strace -qqe '' -p <PID>
Operation not permitted
第二个strace实例);你能确认吗?
(...)"tracee" always means "(one) thread"
并且我确认您提到的错误)。为了让更多的进程以这种方式等待,您必须建立一条链。
wait
循环使用in,以等待终止所有进程:
function anywait()
{
for pid in "$@"
do
wait $pid
echo "Process $pid terminated"
done
echo 'All processes terminated'
}
当所有进程终止时,此功能将立即退出。这是最有效的解决方案。
使用kill -0
in循环,等待终止所有进程+在两次检查之间执行任何操作:
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'
}
反应时间减少到一定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
当进程终止时,使用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 /
在OSX之类的系统上,您可能没有pgrep,因此在按名称查找进程时可以尝试使用以下方法:
while ps axg | grep process_name$ > /dev/null; do sleep 1; done
$
进程名称末尾的符号可确保grep仅将process_name与ps输出中的行末匹配,而不与本身匹配。
/dev/null
,-q
不应该与一起使用grep
。该过程的另一个实例可能是在您的循环正在休眠时开始的,您将永远不知道...
-q
建议是有效的,但正如我在回答中提到的那样,终止$
手段grep不会与名称“命令行中的某处”匹配,也不会与自身匹配。您实际上在OSX上尝试过吗?
Rauno Palosaari针对的解决方案Timeout in Seconds
Darwin
是针对不具有GNU tail
(并非特定于Darwin
)的类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
是轮询间隔(以秒为单位),因此可以根据情况进行更改。