wait命令未等待while循环中进程结束的问题排查与修复
问题分析与修复方案
我之前踩过完全一样的坑!你的脚本最后出现curl输出在脚本退出后才显示的问题,核心原因是管道创建的子shell让后台进程脱离了主shell的控制。
具体原因解释
当你使用head -n 10 urls.txt | while read LINE这种管道写法时,管道右侧的while循环会在一个独立的子shell里运行。你在这个子shell中启动的所有curl后台进程,主shell根本看不到它们的进程ID。
所以当子shell里的循环执行完毕后,主shell里的wait命令完全不知道还有未完成的curl进程在跑,直接跳过等待就结束了脚本。而那些还在运行的curl进程会被系统的init进程接管,它们的输出自然会在脚本退出后才打印出来。
两种修复方法
方法一:用进程替换避免子shell
把管道换成bash的进程替换语法,让while循环在主shell里执行,这样主shell就能追踪所有后台进程:
count=0 # 用进程替换代替管道,while循环在主shell运行 while read LINE; do curl -o /dev/null -s "$LINE" -w "%{time_total}\n" & count=$((count+1)) [ 0 -eq $((count % 3)) ] && wait && echo "process wait" # 每3个进程等待一次 done < <(head -n 10 urls.txt) echo "before wait" wait # 主shell能看到所有后台进程,会等待它们全部完成 echo "after wait"
方法二:手动跟踪后台进程PID
不管子shell,直接记录每个curl进程的PID,最后明确等待这些PID:
count=0 # 用数组保存所有后台进程的PID pids=() head -n 10 urls.txt | while read LINE; do curl -o /dev/null -s "$LINE" -w "%{time_total}\n" & pids+=($!) # 记录当前后台进程的PID count=$((count+1)) # 每3个进程等待一次,然后清空PID数组 [ 0 -eq $((count % 3)) ] && wait "${pids[@]}" && echo "process wait" && pids=() done echo "before wait" wait "${pids[@]}" # 等待剩余未完成的后台进程 echo "after wait"
修复效果验证
用上面两种方法修改后,所有curl的耗时输出都会在after wait之前打印完成,不会再出现脚本退出后才输出的情况。
内容的提问来源于stack exchange,提问作者Lusha Li
相关产品推荐
相关产品推荐

