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.
However, wait
returns an error if I use one more pipe after it.
$ 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)
I can reproduce the wait error every time. I think it has something to do with each pipe being a separate subshell. 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 -T
shows that.
There are relevant questions q1 and q2. But in them there is no pipe usage after the wait
built-in.
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 wait
wait on the previously backgrounded process. For example:
{ bash -c "sleep 5 | false" & wait $! ; } | true
This does not return the same wait error.
There are two key points to observe here:
wait
(a shell built-in) can wait only (the shell's) childrenSo, when you say:
cmd & wait $!
then cmd
is run in your current shell, in background, and wait
(being the shell's built-in) can wait on cmd
's PID, since cmd
is a child of that shell (and therefore a child of wait
).
On the other hand, when you say:
cmd & wait $! | cmd2
then cmd
is still run in your current shell, but the pipe induces a new subshell for wait
(a new bash
process) in which cmd
is not its child, and wait
can not wait on its sibling (a child of its parent).
As an additional clarification of shell's grammar - the &
operator (along with ;
, &&
and ||
) separates pipelines, forming lists. So, a list is a sequence of pipelines, and a pipeline is a sequence of commands separated by |
.
That means the last example above is equivalent to:
cmd & { wait $! | cmd2; }
and not to this:
{ cmd & wait $! ; } | cmd2
which is equivalent to what you have expected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With