Bash - 等待子进程,循环类型之间的差异

时间:2017-07-13 04:53:07

标签: multithreading bash sleep

以下脚本包含两个循环,将进程分叉到后台。它们的行为不同,因为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

2 个答案:

答案 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)"