我正在尝试检查 bash 脚本中正在运行多少个活动进程。这个想法是让 x 个进程保持运行,当一个进程完成时,就会启动下一个进程。
出于测试目的,我设置了这个脚本:
find $HOME/Downloads -name "dummy" &
find $HOME/Downloads -name "dummy" &
find $HOME/Downloads -name "dummy" &
while true
do
pids=()
while read pid; do
echo "PID: $pid"
pids+=("$pid")
done < <(jobs -p)
jobs -p
echo "Active processes: ${#pids[@]}"
if [ ${#pids[@]} -lt 2 ]; then
break
fi
echo "Process(es) still running... ${pids[@]}"
sleep 1
done
但这不起作用,因为jobs -p
即使进程已完成,也会继续返回作业 ID。
以下示例详细说明了该问题:
#!/bin/bash
find $HOME/Downloads -name "dummy" &
find $HOME/Downloads -name "dummy" &
find $HOME/Downloads -name "dummy" &
while true
do
jobs -p # continues to print all 3 jobs
sleep 1
done
如何获得while
循环中的活动作业?
问候,
答案1
虽然wait -n
(根据评论@icarus)在这种特殊情况下工作,但应该注意的是,它$!
包含最后启动的进程的 PID。所以你也可以测试一下:
#!/bin/bash
find $HOME/Downloads -name "dummy" &
p1=$!
find $HOME/Downloads -name "dummy" &
p2=$!
find $HOME/Downloads -name "dummy" &
p3=$!
while true
do
if ps $p1 > /dev/null ; then
echo -n "p1 runs "
else
echo -n "p1 ended"
fi
if ps $p2 > /dev/null ; then
echo -n "p2 runs "
else
echo -n "p2 ended"
fi
if ps $p1 > /dev/null ; then
echo -n "p3 runs "
else
echo -n "p3 ended"
fi
echo ''
sleep 1
done
但parallel
也是一个更好的选择。
答案2
该脚本的问题在于其中没有任何内容将调用等待系统调用之一。通常,在调用 wait 之前,内核将为该进程保留一个条目,因为这是存储子进程的返回代码的地方。如果父进程在子进程之前结束,则子进程将重新设置父进程,通常为 PID 1。系统启动后,PID 1 通常被编程为进入一个循环,仅调用 wait 来收集这些进程的退出值。
wait
重写测试脚本来调用我们得到的shell内置函数
pids=()
find $HOME/Downloads -name "dummy" &
pids+=( $! )
find $HOME/Downloads -name "dummy" &
pids+=( $! )
find $HOME/Downloads -name "dummy" &
pids+=( $! )
echo "Initial active processes: ${#pids[@]}"
for ((i=${#pids[@]}; i>1; i--)) ; do
do
wait -n # Wait for one process to exit
echo "A process exited with RC=$?"
# Note that -n is a bash extension, not in POSIX
# if we have bash 5.1 then we can use "wait -np EX" to find which
# job has finished, the value is put in $EX. Then we can remove the
# value from the pids array.
echo "Still outstanding $(jobs -p)"
sleep 1
done
答案3
我正在尝试检查 bash 脚本中正在运行多少个活动进程。这个想法是让 x 个进程保持运行,当一个进程完成时,就会启动下一个进程。
使用 GNU Parallel 时,它看起来像这样:
parallel -j3 find $HOME/Downloads -name {} ::: "name1" "name2" "name3"
如果您需要运行不同的程序(而不仅仅是不同的参数):
parallel -j3 ::: "find $HOME/Downloads -name name1" "find $HOME/Downloads -name name2" "find $HOME/Downloads -name name3"
如果命令更复杂,请使用 bash 函数:
find1() {
find $HOME/Downloads -name "name1"
}
find2() {
find $HOME/Downloads -name "name2"
}
find3() {
find $HOME/Downloads -name "name3"
}
export -f find1 find2 find3
parallel -j3 ::: find1 find2 find3
如果省略,-j3
它将为每个 CPU 核心运行一项作业。