Bash - 等待子进程,循环类型之间的区别
Bash - waiting on child processes, differences between loop types
下面的脚本包含两个将进程分叉到后台的循环。它们的行为不同,因为 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
一些实验...
这个基于 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
),所以这是一个 内置 问题...
在做了更多研究后,我证实了我的怀疑,管道内部的循环创建了一个子shell。我了解到 wait
只会等待当前 shell 的 children,所以我能想到的最干净的解决方案是确保后台任务和等待在同一个子shell。我通过将循环和 wait
包装在管道内的一组 curly-braces 中来实现这一点。
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)"
下面的脚本包含两个将进程分叉到后台的循环。它们的行为不同,因为 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
一些实验...
这个基于 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
),所以这是一个 内置 问题...
在做了更多研究后,我证实了我的怀疑,管道内部的循环创建了一个子shell。我了解到 wait
只会等待当前 shell 的 children,所以我能想到的最干净的解决方案是确保后台任务和等待在同一个子shell。我通过将循环和 wait
包装在管道内的一组 curly-braces 中来实现这一点。
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)"