在下面我创建一个后台进程并等待它完成。
$ bash -c "sleep 5 | false" & wait $!
[1] 46950
[1]+ Exit 1 bash -c "sleep 5 | false"
$ echo $?
1
这样可以在5秒后返回提示。
但是,wait
如果我之后再使用一个管道,则会返回错误。
$ bash -c "sleep 5 | false" & wait $! | true
[1] 49493
-bash: wait: pid 49493 is not a child of this shell
hbaba@mbp-005063:~/misc$ echo $?
0
hbaba@mbp-005063:~/misc$ ps -T -f
UID PID PPID C STIME TTY TIME CMD
980771313 49493 69771 0 12:56AM ttys056 0:00.00 bash -c sleep 5 | false
980771313 49498 49493 0 12:56AM ttys056 0:00.00 sleep 5
0 49555 69771 0 12:56AM ttys056 0:00.01 ps -T -f
这里发生了什么?
我使用的是bash版本GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)
我每次都可以重现等待错误。 我认为它与每个管道是一个单独的子shell有关。 https://unix.stackexchange.com/a/127346/212862
也许等待$! command在错误的shell中查找子进程。
错误消息提到49493 pid。这确实是bash -c …
命令的正确pid。 ps -T
表明了这一点。
更新
我对&
和|
之间的bash中的运算符优先级存在误解。 @randomir在他的answer中指出了这一点。添加花括号使wait
等待先前的后台进程。例如:
{ bash -c "sleep 5 | false" & wait $! ; } | true
这不会返回相同的等待错误。
答案 0 :(得分:8)
这里有两个要点:
wait
(内置shell)只能等待(shell的)子项所以,当你说:
cmd & wait $!
然后cmd
在当前shell中运行,在后台运行,wait
(作为shell的内置)可以等待cmd
的PID,因为{{1} }是该shell的子级(因此是cmd
的子级)。
另一方面,当你说:
wait
然后cmd & wait $! | cmd2
仍在您当前的shell中运行,但是管道会为cmd
(一个新的wait
进程)引入一个新的子shell,其中{ {1}} 不是它的孩子,bash
不能等待其兄弟(其父级的子女)。
作为shell语法的另一个澄清 - cmd
运算符(以及wait
,&
和;
)分隔管道,形成列表。因此,列表是管道的序列,而管道是由&&
分隔的命令序列。
这意味着上面的最后一个例子相当于:
||
和不到此:
|
这相当于你所期望的。
答案 1 :(得分:0)
Bash手册:
管道中的每个命令都作为一个单独的进程执行(即,在 一个子壳)。
您可以通过以下方式查看:
$ echo "me1: $BASHPID"
me1: 34438
$ sleep 100 &
[1] 34989
$ echo "me2: $BASHPID, child: $!"
me2: 34438, child: 34989
$ true | echo "me3: $BASHPID, child: $!"
me3: 34991, child: 34989
第一个和第二个echo
(me1
和me2
)在顶层shell(34438
)的上下文中执行。 sleep
子进程(34989
)是其子级之一。第三个echo
(me3
)在子shell(34991
)的上下文中执行。 sleep
进程不再是其子女之一。