Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

While reading the cookbook, I started playing with the forking open. I have a small program (below) that forks 3 processes, each of which modify STDIN and send to STDOUT. What I don't understand is why they work in the order that they do.
tee('foo'); tee('bar'); tee('baz'); while (<>) { print } sub tee { my $key = shift; # reopen STDOUT in parent and return return if my $pid = open(STDOUT, "|-"); die "cannot fork: $!" unless defined $pid; $|++; # process STDIN in child while (<STDIN>) { print "$key: $_" or die "tee output failed: $!"; } print "$$ exiting...\n"; exit; # don't let the child return to main! }
When the first child is forked, the STDOUT of the parent is connected to the STDIN of the child, like so:
Parent -> Child1
For the second and third forks I would expect the new child to be inserted between the parent and current child, so that after 3 forks:
Parent -> Child3 -> Child2 - Child1
So I would expected each line of output to be preprended with:
baz: bar: foo:
Instead, each line of output is prepended with:
foo: bar: baz: 
What is the error in my reasoning? How does this actually work? Thanks.

Replies are listed 'Best First'.
Re: Understanding forking open
by tlm (Prior) on Apr 12, 2005 at 15:24 UTC

    Your reasoning is correct up to the very last bit. The filters are applied LIFO, as your diagram correctly indicates. The first filter in this chain (which is the last one added) will spit out a line prepended by 'baz: '; the next filter will prepend 'bar: ' to the output of the previous filter, i.e. a line already beginning with 'baz: ', resulting in a line prepended by 'bar: baz: '. Similarly, the output of the last filter met (the first one invoked) is prepended by 'foo: ' to the output of the previous filter, resulting in a line beginning with 'foo: bar: baz: '.

    the lowliest monk

Re: Understanding forking open
by polettix (Vicar) on Apr 12, 2005 at 15:25 UTC
    The output will not be sent to the terminal until foo actually outputs it. If you want to see it more in depth, I suggest a slight modification to your program following the Unbelievably Obvious Debugging Tip by dreadpiratepeter:
    tee('foo'); tee('bar'); tee('baz'); while (<>) { print } sub tee { my $key = shift; # reopen STDOUT in parent and return return if my $pid = open(STDOUT, "|-"); die "cannot fork: $!" unless defined $pid; $|++; # process STDIN in child while (<STDIN>) { chomp; print "$key: <$_>" or die "tee output failed: $!"; } print "$$ exiting...\n"; exit; # don't let the child return to main! }
    You'll notice that it's exactly as you told: parent is talking to baz, which is talking to bar, which is talking to foo. But each one prepends its name, so you end up with the reverted list.

    Flavio (perl -e "print(scalar(reverse('ti.xittelop@oivalf')))")

    Don't fool yourself.