perlquestion
thiagu_mvt
<p>
I am trying to write a reusable object oriented class for using IPC::Open3 module.<br>
I have couple of problems in dealing with File handles. Can you guys review the code and suggest where it is going wrong or how it can be better ?
<br><br>
Issues need to resolve<br>
-----------------------<br>
1)Want to check whether input filehandle is ready to receive input(current WNOHANG is not helpful when command supplied runs for long time)<br>
2)Issues with output and error file handles in returning buffer text.<br>
</p>
<c>
cmd.pl
--------------
use Task;
my $obj = Task->new("cat","-t");
my $obj = Task->new("sleep 50");
$obj->start();
if($obj->canSendInput)
{
print $obj->sendInput("Practical Extraction Report Language\n", "Visual C plus plus");
}
print "\noutput1:" . $obj->getOutput();
print "\nError:" . $obj->getError();
print "\n";
$obj->stop();
print $obj->sendInput("Second Input");
[thiagu@host1 ~/exe_abs]$ ./cmd.pl
Filehandle GEN0 opened only for output at Task.pm line 89.
Use of uninitialized value in concatenation (.) or string at Task.pm line 118.
1
Filehandle GEN0 opened only for output at Task.pm line 89.
output1:
Error:
Task.pm
----------------
package Task;
use strict;
use warnings;
use IPC::Open3;
use IO::Select;
use IO::Handle;
use POSIX qw(:sys_wait_h);
#Creating Object
sub new {
my($class, $cmd, @args) = @_;
return bless({
cmd => $cmd,
args => \@args,
pid => undef,
stdout => "",
stderr => "",
}, $class);
}
#Running the command with arguments supplied and opening file handles for inout, output and error
sub start {
my $self = shift;
my $cmd_to_exe = "$self->{cmd} "."@{$self->{args}}";
# Reading both output and error filehandles sametime
$self->{'selector'} = IO::Select->new();
$self->{inputfh} = IO::Handle->new();
$self->{outputfh} = IO::Handle->new();
$self->{errorfh} = IO::Handle->new();
{
$self->{pid} = open3($self->{inputfh}, $self->{outputfh}, $self->{errorfh}, $cmd_to_exe);
$self->{'selector'}->add($self->{inputfh}, $self->{outputfh}, $self->{errorfh} );
#print $self->{pid};
return (1);
}
}
sub canSendInput
{
my $self = shift;
my $pid = waitpid($self->{pid}, WNOHANG);
#print $pid;
#return $pid;
($pid==0)?return(1):return(0);
}
#Sending input to command by printing in the input file handler
sub sendInput
{
my $self = shift;
my @input = @_;
if(canSendInput($self))
{
my $kid = waitpid($self->{pid}, WNOHANG);
foreach my $input(@input)
{
(print {$self->{inputfh}} "$input") or return (0);
return (1);
}
}
}
#Retriving output of the command executed for the last input
sub getOutput {
my $self = shift;
my $errCall = shift;
my $outputfh = $self->{outputfh};
my $errorfh = $self->{errorfh};
my $output = "";
my $error = "";
my @ready = $self->{selector}->can_read(1);
foreach my $fh ( @ready )
{
while(sysread($fh, my $text, 1024)) #Reading 1024 bytes in every iteration
{
if(fileno($fh) == fileno($outputfh))
{
$output.= $text;
#print $text;
last if(length($text) < 1024);
}
else
{
$error.= $text;
#print $text;
last if(length($text) < 1024);
}
}
}
#Returning only Error when getOutput is called from getError() and saving output text into object so we can use it later
if($errCall)
{
$self->{stdout} = $output;
$error.=$self->{stderr}; # Updating errors which are not returned so far
$self->{stderr} = "";
return $error;
}
#Returning only Output when getOutput is called directly after saving error text into object so we can use it later
else{
$self->{stderr} = $error;
$output.=$self->{output}; # Updating output which are not returned so far
$self->{stdout}="";
return $output;
}
}
#Retriving error
sub getError {
my $self = shift;
my $error = getOutput($self, 1);
return $error;
}
sub stop {
my $self = shift;
if( defined( $self->{'selector'} ) )
{
$self->{'selector'}->remove( $self->{inputfh}, $self->{outputfh}, $self->{errorfh} );
}
local $SIG{INT} = 'IGNORE';
kill INT => -$$;
}
#Destructor
sub DESTROY {
my $self = shift;
&stop;
}
1;
</c>
<p>
Thanks,
Thiagu
</p>