为何while read循环中启动的后台任务无法被wait命令等待?
管道while循环中wait失效的问题解决
问题原因
你猜的完全正确——管道右侧的while read i是在子shell里运行的。这意味着循环里启动的所有后台进程,都是这个子shell的子进程。而脚本主进程的wait只能等待自己的直接子进程,根本管不到子shell里的后台任务,所以主进程会直接跳过wait,先输出"waiting"和"complete",等子shell里的后台任务陆续跑完,才会输出那些数字。
修复方案
方案1:用进程替换替换管道
把管道换成进程替换,让while循环在主shell里执行,这样后台进程就属于主进程,wait就能正常工作:
while read i; do sleep 1 && echo $i & done < <(seq 5) echo "waiting" wait echo "complete"
<(seq 5)会生成一个临时的文件描述符,while直接读取这个描述符的内容,全程在主shell运行,没有子shell的问题。
方案2:开启Bash的lastpipe选项(Bash 4.2+可用)
开启lastpipe选项后,管道的最后一个命令会在主shell里执行,这样while循环就不会跑到子shell里了:
shopt -s lastpipe seq 5 | while read i; do sleep 1 && echo $i & done echo "waiting" wait echo "complete"
注意:这个选项在交互式shell里可能默认开启,但脚本里必须手动设置,而且只支持Bash 4.2及以上版本。
方案3:把所有逻辑塞进子shell
如果一定要用管道,可以把wait和后续输出都放到子shell的代码块里,这样wait就能管到循环里的后台进程了:
seq 5 | { while read i; do sleep 1 && echo $i & done echo "waiting" wait echo "complete" }
这种方式下,整个逻辑都在子shell里运行,wait能正常等待,但要注意子shell里的变量修改不会同步到主shell。
内容的提问来源于stack exchange,提问作者EJam
相关产品推荐
相关产品推荐

