7stud has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

Do pipes add a layer of buffering? Here is a simple program:

2perl.pl:
#!/usr/bin/env perl use strict; use warnings; use 5.010; say 'hello'; say 'I need a rest. Sleeping...'; sleep 10;

When I run that program, I see two lines of output, and then the program hangs while it sleeps.

The docs say this about open():

If the filename begins with '|', the filename is interpreted as a command to which output is to be piped, and if the filename ends with a '|', the filename is interpreted as a command which pipes output to us.

Here is a program that executes the previous program:

open (my $INFILE, '2perl.pl |') or die "couldn't start program: $!"; while (<$INFILE>) { print; } close $INFILE;

In this case, the program hangs immediately, and it's only after the first program finishes sleeping(and executing) that any output is displayed. Therefore, it seems like the pipe is adding buffering to a program that was originally unbuffered.

However, if I add the following line to the first program:

$| = 1;

then run the second program, the output from the first program is immediately displayed and then the program hangs. That result makes it seem like the first program buffers by default, and the $|=1 turns off the buffering.

So how do you explain that apparent contradiction? Why do I need to set $| to 1 when the original program didn't seem to buffer when I ran it directly? What if I can't add $|=1 to the first program?

Replies are listed 'Best First'.
Re: open() with a pipe
by ikegami (Patriarch) on Nov 30, 2009 at 07:46 UTC

    Like most programs, Perl's STDOUT is line buffered (flushed on "\n") when connected to a terminal and fully buffered otherwise. $|=1; will turn off STDOUT's buffering.

    What if I can't add $|=1 to the first program?

    Output buffering is done by the process. The OS is not involved. Therefore, there's nothing you can do about it (unless if provides a mechanism to configure its buffering).

    Now, if the child switches to line buffering when connected to a terminal, you could force it to switch by fooling it into thinking it's connected to a terminal. This is done by using a pseudo-tty (pty). IPC::Run gives you easy access to those. If you want to be interactive with the child, maybe Expect is more appropriate.

      Thank you.