Bash中是否有内置功能等待进程完成?
wait
命令只允许等待子进程完成。
我想知道在继续执行任何脚本之前是否有任何方法可以等待任何进程完成。
执行此操作的机械方法如下,但我想知道Bash中是否有任何内置功能。</ p>
while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done
答案 0 :(得分:80)
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)
答案 1 :(得分:78)
没有内置。在循环中使用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 "
。
答案 2 :(得分:49)
如果进程由root(或其他)拥有,我发现“kill -0”不起作用,所以我使用了pgrep并提出:
while pgrep -u root process_name > /dev/null; do sleep 1; done
这样做的缺点可能是匹配僵尸进程。
答案 3 :(得分:31)
如果进程不存在,或者它是僵尸,则此bash脚本循环结束。
PID=<pid to watch>
while s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do
sleep 1
done
编辑:was given below上面的脚本Rockallite。谢谢!
我的orignal回答适用于Linux,依赖于procfs
,即/proc/
。我不知道它的便携性:
while [[ ( -d /proc/$PID ) && ( -z `grep zombie /proc/$PID/status` ) ]]; do
sleep 1
done
它不仅限于shell,但操作系统本身没有系统调用来监视非子进程终止。
答案 4 :(得分:12)
FreeBSD和Solaris有这个方便的pwait(1)
实用程序,它完全符合您的要求。
我相信,其他现代操作系统也有必要的系统调用(例如,MacOS实现了BSD&#39; kqueue
),但并非所有操作系统都可以从命令行使用它。
答案 5 :(得分:10)
来自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.
答案 6 :(得分:5)
好的,似乎答案是 - 不,没有内置工具。
将/proc/sys/kernel/yama/ptrace_scope
设置为0
后,可以使用strace
程序。可以使用进一步的开关使其静音,以便它实际上被动地等待:
strace -qqe '' -p <PID>
答案 7 :(得分:5)
所有这些解决方案都在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。
答案 8 :(得分:3)
我发现了一个等待任何PID的小工具,并在PID死亡时立即返回。
https://github.com/izabera/waiter
这是一个可以用gcc编译的小文件。
{{1}}
它的工作方式与调试器附加到进程时的工作方式相同。
答案 9 :(得分:2)
没有内置功能可以等待任何进程完成。
您可以将kill -0
发送给找到的任何PID,这样您就不会对ps
中仍然可见的僵尸和东西感到困惑(同时仍使用{{1}检索PID列表})。
答案 10 :(得分:1)
有同样的问题,我解决了杀死进程然后等待每个进程完成使用PROC文件系统的问题:
while [ -e /proc/${pid} ]; do sleep 0.1; done
答案 11 :(得分:0)
在像OSX这样的系统上你可能没有pgrep所以你可以在按名称查找进程时尝试这个appraoch:
while ps axg | grep process_name$ > /dev/null; do sleep 1; done
进程名称末尾的$
符号确保grep只将process_name与ps输出中的行末匹配,而不是自身。
答案 12 :(得分:0)
在进程终止时,使用inotifywait监视一些已关闭的文件。示例(在Linux上):
yourproc >logfile.log & disown
inotifywait -q -e close logfile.log
-e指定要等待的事件,-q表示仅在终止时输出最少。在这种情况下,它将是:
logfile.log CLOSE_WRITE,CLOSE
单个等待命令可用于等待多个进程:
yourproc1 >logfile1.log & disown
yourproc2 >logfile2.log & disown
yourproc3 >logfile3.log & disown
inotifywait -q -e close logfile1.log logfile2.log logfile3.log
inotifywait的输出字符串将告诉您哪个进程终止。这仅适用于“真实”文件,不适用于/ proc /
中的内容答案 13 :(得分:0)
Rauno Palosaari针对Timeout in Seconds
Darwin
的解决方案对于没有GNU tail
的类UNIX操作系统(不是特定于Darwin
)来说是一个很好的解决方法。但是,根据类似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
是轮询间隔(以秒为单位),因此可以根据情况进行更改。
答案 14 :(得分:-1)
在循环中使用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'
}
反应时间减少到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。
答案 15 :(得分:-1)
Fedora/Cent Os 使用 yum install 时同样的问题,用 Ctrl+Z 中断它。
使用这个命令:
sudo cat /var/cache/dnf/*pid
如果有任何pid,您可以手动删除它:
sudo nano /var/cache/dnf/*pid
crtl+x 后跟 Y 和 Enter 退出 nano 编辑器(您可以使用任何想要打开的编辑器)。
如果之后,删除一些可能仍然存在的 tmp 文件:
sudo systemd-tmpfiles --remove dnf.conf