我有一个在我的主Bourne Shell脚本中调用的命令CMD需要很长时间才能执行完。
我想按照以下方式修改脚本:
- 将命令CMD作为后台进程并行运行(
CMD &
)。 - 在主脚本中,有一个循环来监视每隔几秒钟生成的命令。该循环还会打印一些消息到标准输出,指示脚本的进度。
- 当生成的命令终止时退出循环。
- 捕获并报告生成的进程的退出代码。
有人能给我提供实现这个需求的提示吗?
# run your stuff
unset PID
for process in one two three four
do
( sleep $((RANDOM%20)); echo hello from process $process; exit $((RANDOM%3)); ) & 2>&1
PID+=($!)
done
# (optional) report on the status of that stuff as it exits
for pid in "${PID[@]}"
do
( wait "$pid"; echo "process $pid complemted with exit status $?") &
done
# (optional) while we wait, monitor that stuff
while ps --pid "${PID[*]}" --ppid "${PID[*]}" --format pid,ppid,command,pcpu
do
sleep 5
done | xargs -i date '+%x %X {}'
# return non-zero if any are non zero
SUCCESS=0
for pid in "${PID[@]}"
do
wait "$pid" && ((SUCCESS++)) && echo "$pid OK" || echo "$pid returned $?"
done
echo "success for $SUCCESS out of ${#PID} jobs"
exit $(( ${#PID} - SUCCESS ))
${#PID}
应该改为 ${#PID[@]}
。 - Troels Ynddal[ -z "$(jobs -p)" ]
。#!/bin/bash
exec 3<> <(:)
{ sleep 15 ; echo "sleep/exit $?" >&3 ; } &
while read -u 3 -t 1 -r STAT CODE || STAT="timeout" ; do
echo "stat: ${STAT}; code: ${CODE}"
if [ "${STAT}" = "sleep/exit" ] ; then
break
fi
done
pgrep -P $$
很容易检查哪些子PID仍在运行,但我想出了以下解决方案来检查已经过期的那些PID的退出状态:cmd1() { sleep 5; exit 24; }
cmd2() { sleep 10; exit 0; }
pids=()
cmd1 & pids+=("$!")
cmd2 & pids+=("$!")
lasttimeout=0
for timeout in 2 7 11; do
echo -n "interval-$timeout: "
sleep $((timeout-lasttimeout))
# you can only wait on a pid once
remainingpids=()
for pid in ${pids[*]}; do
if ! ps -p $pid >/dev/null ; then
wait $pid
echo -n "pid-$pid:exited($?); "
else
echo -n "pid-$pid:running; "
remainingpids+=("$pid")
fi
done
pids=( ${remainingpids[*]} )
lasttimeout=$timeout
echo
done
输出结果为:
interval-2: pid-28083:running; pid-28084:running;
interval-7: pid-28083:exited(24); pid-28084:running;
interval-11: pid-28084:exited(0);
$pids
更改为字符串变量而不是数组,以简化事情。如果您只想并行运行固定数量的命令,并确保错误不被忽略,您可以选择以下非常简单的选项:
#!/bin/bash
set -e
python3 -c "import time; import sys; time.sleep(1); sys.exit(1)" &
python3 -c "import time; import sys; time.sleep(3); sys.exit(0)" &
wait -n
wait -n
wait -n
等待下一个作业完成并返回其退出码。由于我们使用了 set -e
,它将导致整个脚本失败。
请注意,它仍然会让其他作业在后台运行。如果你不希望这样,可以尝试以下方法:
{ wait -n && wait -n ; } || { wait; exit 1; }