tags:

views:

70

answers:

5

Hi, I know I can do this,


------
open(F,">",\$var);
print F "something cool";
close(F);
print $var;
------

or this,

 

open(F, "| ./prog1 | ./prog2 > tmp.file");
print F "something cool";
close(F);

but is it possible to combine these? The semantics of what I'd like to do should be clear from the following,


open(F,"|./prog1 | ./prog2", \$var);
print F "something cool";
close(F);
print $var;

however the above clearly won't work.

A few minutes of experimenting and googling seems to indicate that this is not possible, but I'd like to know if I'm stuck with using the `` to capture the output.

A: 

What you want to do is open the file handle as you have, read the contents to a variable, append whatever you want to that variable and then set the environment variable as is stated here to the full string that you have just generated.

You were almost there and I hope that helps. :)

Robert Massaioli
hmm, i know i can slurp the file i want to feed to the pipe into a variable and then send it all at once - think this is what you are suggesting, right? but i want to do this line by line which i guess is impossible.
blackkettle
Yeah, I don't know a way to do it line by line.
Robert Massaioli
To do this line by line use a `while` loop to process the input handle on a line by line basis and print to the output handle. `print OUT $_ while <IN>;`
daotoad
A: 

It seems that this is not possible as perl does not support 'pipe at both ends'. More info here,

http://perldoc.perl.org/perlipc.html

Corion over at perlmonks.org answered this for me.

blackkettle
Did you post to SOPW or ask in chatterbox? If SOPW, please post a link.
daotoad
I am frequently surprised by how small the set of things that are not possible in Perl is. This is another example of something that is not not possible: see daotoad's suggestion for a tied variable.
mobrule
it was the chatterbox, sorry
blackkettle
@mobrule I'm fairly certain at this point that this is not possible, at least not the way that I'd like to do it, and the reason I was given was basically that this would have a high chance of producing buffering issues. I tried daotoad's example and it does not work (see comment). Anyway this isn't a dig against perl - which I quite love, I was just curious.
blackkettle
A: 
  • IPC::Open2 might do the trick, see man perlipc. More likely than not, however, you will deadlock your program that way. It's safe though, as long as the data in the pipe is smaller than PIPE_BUF (on Linux 4096), only written once, and the write-side immediately closed after writing the data and before reading the result.

  • You might try to rewrite your program to fork such that you end up with: YourProg | ./prog1 | ./prog2 | YourProgForked.

  • On modern Linux you can use /dev/shm to host a temporary file entirely in virtual memory.

edgar.holleis
+1  A: 

You can achieve your goal a couple of ways. None of them quite as easy as piling extra stuff into open.

You can use IO::Tee or other similar module, or even write your own tee style subroutine:

my $var ='';

open my $vh, '>', \$var or die "oh noes $!\n";
open my $ph, '| ./prog1 | ./prog2' or die "Uh oh $!\n";

my @handles = ($vh, $ph);

print_to_all( \@handles,
    "I like cheese.\n",
    "I like teas.\n",
);

print $var;    

sub print_to_all {
     my $handles = shift;

     for my $h ( @$handles ) {
         print $h @_;
     }

     return;
}

Another way to go would be to make a tied variable that acts as a file handle and a scalar at the same time.

daotoad
In the above example $var will end up just containing the original strings, while the result of the pipe command will be printed to STDOUT. I was curious about getting the pipe command output directly into $var, and I don't think this will do the trick. I tried running this code with my own prog1, prog2, but at the end it ends up with $var containing only the original concatenated input strings.
blackkettle
A: 

What I actually wanted to do is not possible in perl.

blackkettle