perl - "tee" the qx operator
I'm working on a script that currently has:
my @files = `$some_command`;
print @files;
chomp @files;
foreach my $file (@files)
{
process($file);
}
It works correctly, but the some_command
part takes the majority of the script's time. And during that time, nothing appears on stdout, because Perl has redirected output from some_command
to populate the @files
array. It only gets printed when some_command
is done and Perl moves on to print @files;
.
Is there some clever way to change this code so that the output of some_command
appears as it is executing? I could maybe try something like this using tee(1)
:
my $tmpfile = File::Temp->new();
system("$some_command | tee " . $tmpfile->filename);
my @files;
{ local $/ = undef; @files = split /\s/, <$tmpfile>; }
But I'd rather avoid the temporary file stuff if t开发者_运维技巧here's a simpler solution.
You could open the handle and manually populate the array yourself while printing the lines as you go.
Something like this would probably work,
open my $fh, '-|', $some_command;
while(<$fh>)
{
print $_;
push @files, $_;
}
close $fh;
You could skip the qx()
operator and open a filehandle to the process's output stream directly. This code in functionally equivalent to my @files = qx($some_command)
:
my @files = ();
open my $proc_fh, "$some_command |";
while (<$proc_fh>) {
push @files, $_;
}
close $proc_fh;
but inside the while loop you can do whatever you want with $_
:
while (<$proc_fh>) {
print "INPUT: $_\n";
push @files, $_;
}
An important consideration is the output buffering behavior of $some_command
. If that command buffers its output, then your $proc_fh
handle will not receive any input until a large block of data is available.
The File::Tee
module looks like it could do what you want. There are examples there of redirecting STDOUT when running system()
. I haven't used it, so I can't give more concrete examples, but it looks like this would be a good jumping-off point for you.
You could also open your command as a file descriptor and read the output as the command is producing it. Something like this (taken from http://www.netadmintools.com/art269.html):
#!/usr/bin/perl
open (PINGTEST, "/bin/ping -c 5 netadmintools.com |");
$i=1;
while (<PINGTEST>){
print "Line # ".$i." ".$_;
$i++;
}
print "All done!\n";
close PINGTEST;
Capture::Tiny is probably exactly what you want.
use Capture::Tiny qw/ tee tee_merged /;
($stdout, $stderr) = tee {
# your code here
};
$merged = tee_merged {
# your code here
};
精彩评论