bash 如果之后使用管道,为什么等待会生成“<pid> 不是此 shell 的子级”错误?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46752794/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 16:31:40  来源:igfitidea点击:

Why does wait generate “<pid> is not a child of this shell” error if a pipe is used afterwards?

linuxbashshellsubprocesspipe

提问by Hakan Baba

In the following I create a background process and wait for it to complete.

下面我创建一个后台进程并等待它完成。

$ bash -c "sleep 5 | false"  &  wait $!
[1] 46950
[1]+  Exit 1                  bash -c "sleep 5 | false"
$ echo $?
1

This works and the prompt returns after 5 seconds.

这有效并且提示在 5 秒后返回。

However, waitreturns an error if I use one more pipe after it.

但是,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

What is happening here?

这里发生了什么?



I am using bash version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)

我正在使用 bash 版本 GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)

I can reproduce the wait error every time. I thinkit has something to do with each pipe being a separate subshell. https://unix.stackexchange.com/a/127346/212862

我每次都可以重现等待错误。我认为这与每个管道都是一个单独的子外壳有关。https://unix.stackexchange.com/a/127346/212862

Maybe the wait $! command looks for the child process in the wrong shell. The error message mentions the 49493 pid. That is indeed the right pid for the bash -c …Command. The ps -Tshows that.

也许等待$!命令在错误的 shell 中查找子进程。错误消息提到了 49493 pid。这确实是bash -c …命令的正确 pid 。这ps -T表明。

There are relevant questions q1and q2. But in them there is no pipe usage after the waitbuilt-in.

有相关的问题q1q2。但是在它们中,wait内置之后没有管道使用。

Update

更新

I had a misunderstanding about operator precedence in bash between the &and |. @randomir pointed that out in his answer. Adding curly braces makes the waitwait on the previously backgrounded process. For example:

我对 bash 中&和之间的运算符优先级有误解|。@randomir 在他的回答中指出了这一点。添加花括号使wait之前的后台进程等待。例如:

{ bash -c "sleep 5 | false"  &  wait $! ; } | true

This does not return the same wait error.

这不会返回相同的等待错误。

回答by randomir

There are two key points to observe here:

这里有两个关键点需要注意:

  • wait(a shell built-in) can wait only (the shell's) children
  • each command in a pipeline runs in a separate subshell
  • wait(内置外壳)只能等待(外壳的)孩子
  • 管道中的每个命令都在单独的子 shell 中运行

So, when you say:

所以,当你说:

cmd & wait $!

then cmdis run in your current shell, in background, and wait(being the shell's built-in) can wait on cmd's PID, since cmdis a child of that shell (and therefore a child of wait).

thencmd在您当前的 shell 中在后台运行,并且wait(作为 shell 的内置)可以等待cmd的 PID,因为它cmd是该 shell 的子级(因此是 的子级wait)。

On the other hand, when you say:

另一方面,当你说:

cmd & wait $! | cmd2

then cmdis still run in your current shell, butthe pipe induces a new subshell for wait(a new bashprocess) in which cmdis not its child, and waitcan not wait on its sibling(a child of its parent).

那么cmd仍然是在当前的shell中运行,管道引起一个新的子shell wait(新bash工艺),其中cmd不是它的孩子,并且wait可以在它的兄弟迫不及待(其父母的孩子)。

As an additional clarification of shell's grammar - the &operator (along with ;, &&and ||) separates pipelines, forming lists. So, a listis a sequence of pipelines, and a pipelineis a sequence of commands separated by |.

作为 shell 语法的额外说明 -&运算符(连同;,&&||)分隔管道,形成列表。因此,列表是一系列管道,而管道是由 分隔的命令序列|

That means the last example above is equivalent to:

这意味着上面的最后一个例子相当于:

cmd & { wait $! | cmd2; }

and notto this:

不是这个:

{ cmd & wait $! ; } | cmd2

which is equivalent to what you have expected.

这相当于您的预期。

回答by Renaud Pacalet

Bash manual:

Bash手册:

Each command in a pipeline is executed as a separate process (i.e., in a subshell).

管道中的每个命令都作为单独的进程(即,在子外壳中)执行。

You can check this with:

您可以通过以下方式检查:

$ 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

The first and second echo(me1and me2) are executed in the context of the top shell (34438). The sleepchild process (34989) is one of its children. The third echo(me3) is executed in the context of a subshell (34991). The sleepprocess is not one of its children any more.

第一个和第二个echo( me1and me2) 在顶层 shell ( 34438)的上下文中执行。该sleep子进程(34989)是它的一个子。第三个echo( me3) 在子外壳 ( 34991)的上下文中执行。该sleep进程不再是它的子进程之一。