Bash脚本中如何等待所有后台进程完成?wait命令失效问题
如何等待所有嵌套的后台工作进程完成?
问题背景
需要并发运行2个工作负载,每个工作负载后台启动,且每个负载包含3个后台运行的run_sleep.sh进程。期望所有进程完成后再执行后续逻辑,但现有脚本要么直接返回,要么无法正确等待所有进程。
现有脚本的问题
脚本1的问题
for wl_num in 1 2 ; do echo "... Workload $wl_num" { for i in `seq 1 3` ; do echo ...... workload process $i . run_sleep.sh 300 & done } & done wait echo ... done waiting for both workloads
这里的wait仅会等待外层的2个子shell(即{...}&启动的进程),但子shell内部启动的run_sleep.sh &进程并不会被主shell的wait追踪。子shell执行完内部的for循环后就会退出,里面的后台进程变成孤儿进程,主脚本的wait执行完就直接继续后续逻辑,不会等这些孤儿进程。
脚本2的问题
for wl_num in 1 2 ; do echo "... Workload $wl_num" { for i in `seq 1 3` ; do echo ...... workload process $i . run_sleep.sh 5 & pids[${i}=$! done } & wlpids[${db_num}]=$! done for pid in ${pids[*]} ; do wait $pid done for pid in ${wlpids[*]} ; do wait $pid done echo ... done waiting for both workloads
- 语法错误:
pids[${i}=$!缺少闭合的],db_num变量未定义; - 逻辑错误:子shell(
{...}&)内部定义的pids数组无法被主shell访问,因为子shell有独立的进程空间,变量不会共享,所以主shell的pids数组是空的,无法等待内部进程。
解决方案
方案1:在主shell中记录所有进程的PID
直接在主循环里启动所有后台进程,把每个进程的PID都记录到主shell的数组中,最后统一wait所有PID:
# 初始化PID数组 pids=() for wl_num in 1 2 ; do echo "... Workload $wl_num" for i in `seq 1 3` ; do echo ...... workload process $i . run_sleep.sh 300 & # 将每个后台进程的PID加入数组 pids+=($!) done done # 等待所有记录的PID完成 for pid in "${pids[@]}" ; do wait $pid done echo ... done waiting for all processes
这个方法最直接,所有进程的PID都在主shell中追踪,确保不会遗漏任何进程。
方案2:让子shell内部等待自身的后台进程
如果必须保留工作负载的子shell结构,可以让每个子shell内部先等待自己启动的所有进程完成,这样主shell等待子shell即可:
# 初始化工作负载子shell的PID数组 wlpids=() for wl_num in 1 2 ; do echo "... Workload $wl_num" { # 子shell内部的PID数组 local sub_pids=() for i in `seq 1 3` ; do echo ...... workload process $i . run_sleep.sh 300 & sub_pids+=($!) done # 子shell内部等待所有自身启动的进程 for pid in "${sub_pids[@]}" ; do wait $pid done } & # 记录子shell的PID wlpids+=($!) done # 主shell等待所有工作负载子shell完成 for pid in "${wlpids[@]}" ; do wait $pid done echo ... done waiting for both workloads and all their processes
这里每个子shell会先等自己内部的所有后台进程完成后才退出,所以主shell等待子shell就等同于等待该负载下的所有进程完成。
注意事项
- 不要在子shell中定义需要主shell访问的变量,子shell的变量无法传递回主shell;
- 使用
"${array[@]}"而不是${array[*]}来遍历数组,避免PID包含特殊字符时出现问题; - 如果
run_sleep.sh是独立脚本,使用./run_sleep.sh而不是. run_sleep.sh(后者是source,会在当前shell执行,可能影响变量环境),如果确实需要source,确保脚本没有退出逻辑。
内容的提问来源于stack exchange,提问作者Kanji174
相关产品推荐
相关产品推荐

