如何等待多个后台作业完成,按结束顺序处理且不丢失退出码?
问题背景
我有一段脚本会启动多个后台子进程,之后等待它们全部完成。目前的实现是通过循环对每个PID调用wait命令:
for pid in "${!PIDs[@]}" do if wait $pid then log ${PIDs[$pid]} completed successfully else log WARNING: ${PIDs[$pid]} failed errors+=1 fi done
这种方式能分析并处理进程失败,但处理顺序是按照PID列表的顺序,而非进程实际完成的顺序——比如第5个进程可能最先完成,却要等前4个处理完毕后才会处理它的退出状态。
我了解到sh的wait命令有两种模式:
- 直接使用
wait会等待所有后台作业完成,但始终返回“成功”,丢失后台进程的退出码; wait PID会等待指定进程,可获取退出码,但仅能等待单个进程。
想问:bash相较于旧版sh是否有改进?能否让bash的wait命令在任意后台进程完成时返回,同时提供该进程的PID与退出码?
底层C函数waitpid及其同类函数通过传入PID=-1可实现此功能,但我在bash中尝试时出现了错误。
解决方案
Bash 4.3及以上版本提供了wait -n选项,完全匹配你的需求:
wait -n会等待任意一个后台子进程完成,完成后立即返回;返回后,$!会被设置为该完成进程的PID,$?为该进程的退出码。
你可以基于这个特性改写脚本,实现按进程实际完成顺序处理退出状态:
# 假设PIDs是你存储的后台进程PID与对应描述的关联数组 declare -A PIDs=( [1234]="task1" [5678]="task2" # 更多进程... ) local errors=0 # 循环等待所有进程完成 while (( ${#PIDs[@]} > 0 )); do # 等待任意一个后台进程结束 wait -n local completed_pid=$! local exit_code=$? # 处理该进程的退出状态 if [[ -n "${PIDs[$completed_pid]}" ]]; then if (( exit_code == 0 )); then log "${PIDs[$completed_pid]} completed successfully" else log "WARNING: ${PIDs[$completed_pid]} failed with exit code $exit_code" (( errors++ )) fi # 从数组中移除已处理的进程 unset "PIDs[$completed_pid]" fi done
核心细节
wait -n是Bash 4.3引入的特性,若你的Bash版本低于此,该选项无法使用;- 每次
wait -n仅处理一个完成的进程,因此需要通过循环直到所有PID都被处理完毕; - 这个行为完全对应C语言中
waitpid(-1, &status, 0)的功能,能实时响应最先完成的进程。
内容的提问来源于stack exchange,提问作者Mikhail T.
相关产品推荐
相关产品推荐

