
The behaviour of Linux pipes can be indeterministic - no_gravity
http://www.gibney.de/the_output_of_linux_pipes_can_be_indeter
======
devnonymous
Firstly, behavior of pipes on the command line (ie: the effect of the
character '|' as opposed to the pipe(2) call) is the behavior of the shell
feature rather than the OS.

Secondly, if I'm not mistaken the behavior doesn't have anything to do with
the pipe. It is the scheduling of the subshell (created by () ) which might
explain what you're seeing. IOW, when you create the pipeline you are
executing "echo blue" in the same shell and "(echo red; echo green)" in a
different (child) shell. Execution of these are independent. Since "echo blue"
doesn't have to wait for I/O on the pipeline, it runs and exits.

Thirdly, your 1>&2 is perhaps misplaced, ie: if you were expecting red also to
be printed you probably want (echo red; echo green) 1>&2 ...

Fourtly, by 1>&2 you are redirecting stdout to stderrr, which in a pipeline
implies whatever is received first goes to the _parent_ shell's stderr _not_
to the pipeline (if that was your intention).

IOW, the entire this is the same as executing

/bin/bash -c "echo red; echo green >& 2" >/dev/null ; echo blue

I'd expect the result to be no different

[1] I am aware that bash has echo as a builtin, as opposed to /bin/echo --
which would results in an exec(3), possibly other shells do as well.

~~~
no_gravity

        if you were expecting red also to be printed
    

I did not expect that. I expected the output to _always_ be "blue green".

The surprising thing here is that about 1% of the time, the command outputs
something else. It seems to be some race condition that happens in the
handling of the pipeline.

~~~
devnonymous
> I did not expect that. I expected the output to always be "blue green".

Why do you expect that ? What ought to prevent the execution of the echo in
the subshell in front of the pipe ?

> It seems to be some race condition that happens in the handling of the
> pipeline.

What exactly is the pipeline achieving in your opinion ?

~~~
no_gravity
I have written out my theories on the expected and observed behaviour on the
page now. I also added a link to a long discussion this spawned on lobste.rs
which is pretty interesting.

~~~
devnonymous
I'm not sure whether you've understood the underlying ideas still.

Peter's post on lobste.rs
([https://lobste.rs/s/mb14bg/what_should_be_output_this_comman...](https://lobste.rs/s/mb14bg/what_should_be_output_this_command_echo#c_mjvjtk)
) is far more comprehensive and clear imo, than any of the other explanations
(including mine), but the idea is essentially the same.

* There is no blocking on buffers involved. The 2 ends of the pipe are simultaneously run. One in a subshell and one in the current shell.

* If the one in the current shell (right side of the pipe) is executed and finished first, blue is sent to the stderr (ie: the console), a SIGPIPE is sent to the subshell process (thus ensuring red is never printed) _but_ if SIGPIPE is ack'd after echo green is sent to the stderr, you also see green, else you see only blue

* If the one in the subshell is executed first then red is sent to the stdout of the subshell (which is the pipe, but nobody is reading the pipe, so it just lies in there and eventually is lost, since the other end of the pipe will eventually be closed without any read()), green is sent to the stderr and then blue.

I feel you believe there subshell process is blocked because nothing is
reading from the other side. This is incorrect. The pipe buffer will be
written to until the "pipe size" (try ulimit -a or ulimit -p). The write end
will _not_ be blocked until then.

Some reading:

    
    
      https://www.gnu.org/software/bash/manual/html_node/Command-Grouping.html#Command-Grouping
      https://www.gnu.org/software/bash/manual/html_node/Command-Execution-Environment.html#Command-Execution-Environment
      http://man7.org/linux/man-pages/man7/pipe.7.html  (see I/O on pipes)

~~~
devnonymous
I see you've now updated the post and got the idea. Small nitpick tho'.

> When the right side of the pipe ends, the shell kills the left side.

That's technically incorrect. The shell sends SIGPIPE to the subshell. SIGPIPE
can be trapped/ignored (man trap) and although the default disposition is to
kill the process that receives the signal, this might not be always true.

For instance, what do you suppose would happen with this:

    
    
        (x=42; echo $x 1>&2 ) | echo blue
    

versus:

    
    
        (trap '' PIPE; x=42; echo $x 1>&2 ) | echo blue
    

in a loop ?

