以下脚本包含两个循环,将进程分叉到后台。它们的行为不同,因为for
循环产生预期结果(等待10个子进程完成),但while
循环不会产生相同的结果。基于pstree
的输出,很明显while
循环不会导致子进程成为当前shell的子进程。我怀疑这是由于管道?
$ /tmp/wtf
1499921102 - for loop - start
wtf(2797)─┬─pstree(2810)
├─sleep(2800)
├─sleep(2801)
├─sleep(2802)
├─sleep(2803)
├─sleep(2804)
├─sleep(2805)
├─sleep(2806)
├─sleep(2807)
├─sleep(2808)
└─sleep(2809)
1499921102 - for loop - waiting
1499921112 - for loop - done
1499921112 - while loop - start
wtf(2797)───pstree(2826)
1499921112 - while loop - waiting
1499921112 - while loop - done
$ cat /tmp/wtf
#!/bin/bash
set -m
set -e
set -u
printf "%s - for loop - start\n" "$(date +%s)"
for i in `seq 1 10`
do
sleep 10 &
done
pstree -pl $$
printf "%s - for loop - waiting\n" "$(date +%s)"
wait
printf "%s - for loop - done\n" "$(date +%s)"
printf "%s - while loop - start\n" "$(date +%s)"
seq 1 10 | while read i
do
sleep 10 &
done
pstree -pl $$
printf "%s - while loop - waiting\n" "$(date +%s)"
wait
printf "%s - while loop - done\n" "$(date +%s)"
如何让当前进程等待不是直接子进程的进程?尝试将流程ID传递到wait
并未成功:
wait $(seq 1 10 | while read i
do
sleep 10 &
echo $!
done | tr '\n' ' ')
/tmp/wtf: line 22: wait: pid 2866 is not a child of this shell
/tmp/wtf: line 22: wait: pid 2867 is not a child of this shell
/tmp/wtf: line 22: wait: pid 2868 is not a child of this shell
/tmp/wtf: line 22: wait: pid 2869 is not a child of this shell
/tmp/wtf: line 22: wait: pid 2870 is not a child of this shell
/tmp/wtf: line 22: wait: pid 2871 is not a child of this shell
/tmp/wtf: line 22: wait: pid 2872 is not a child of this shell
/tmp/wtf: line 22: wait: pid 2873 is not a child of this shell
/tmp/wtf: line 22: wait: pid 2874 is not a child of this shell
/tmp/wtf: line 22: wait: pid 2875 is not a child of this shell
答案 0 :(得分:0)
一些实验......
这个基于while
的版本似乎有效:
seq 1 10 | while read i; do sleep 10 & echo $!; done | xargs sh -c wait
这也有效:
sh -c wait $(seq 1 10 | while read i; do sleep 10 & echo $!; done)
注意:
tr
是不必要的。wait
是一个shell内置命令,xargs
无法看到它
sh -c
。sh -c
是必要的,(即使没有xargs
),所以这是内置问题...... 答案 1 :(得分:0)
在做了一些研究之后,我确认我怀疑管道内的循环会创建一个子shell。我学到的是wait
只会等待当前shell的子项,所以我能想到的最干净的解决方案是确保后台任务和等待都在同一个子shell中。我通过将循环和wait
包装在管道内的一组花括号中来实现这一点。
printf "%s - while loop - start\n" "$(date +%s)"
seq 1 10 | {
while read i
do
sleep 10 &
done
pstree -pl $$
printf "%s - while loop - waiting\n" "$(date +%s)"
wait
}
printf "%s - while loop - done\n" "$(date +%s)"