Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Queries on open3

by thiagu_mvt (Sexton)
on Apr 22, 2009 at 19:13 UTC ( #759379=perlquestion: print w/replies, xml ) Need Help??

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

I am trying to write a reusable object oriented class for using IPC::Open3 module.
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 ?

Issues need to resolve
-----------------------
1)Want to check whether input filehandle is ready to receive input(current WNOHANG is not helpful when command supplied runs for long time)
2)Issues with output and error file handles in returning buffer text.

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 l +ine 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}, $sel +f->{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 sav +ing output text into object so we can use it later if($errCall) { $self->{stdout} = $output; $error.=$self->{stderr}; # Updating errors which are not returne +d 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 returne +d 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->{outputf +h}, $self->{errorfh} ); } local $SIG{INT} = 'IGNORE'; kill INT => -$$; } #Destructor sub DESTROY { my $self = shift; &stop; } 1;

Thanks, Thiagu

Replies are listed 'Best First'.
Re: Queries on open3
by ikegami (Patriarch) on Apr 22, 2009 at 19:48 UTC

    Some notes before I get started:

    • Could you remove your <pre>..</pre> tags please? Use <p> at the start of paragraphs. Use <c>..</c> or <code>..</code> around computer text, code and data..

    • You seem to be reinventing parts of IPC::Run. Is there really a need for this module?

    I have couple of problems in dealing with Filehandlers

    They are called "file handles". Note the space and the lack of "r".

    Want to check whether input filehandler is ready to receive input

    can_write and select in IO::Select will tell you that on non-Windows systems. I don't think it's possible in Windows. (Alternate paradigms are used.)

    Issues with output and error filehandlers in returning buffer text

    Sorry, but you can't control whether another process buffers its output or not unless the specific program provides you a specific means (e.g. a command line option) of doing so.

    However, you can fool most by creating a pseudo-tty. See IPC::Run and IO::Pty.

    Filehandle GEN0 opened only for output at Task.pm line 89.

    You should have two selectors. One for inputs and one for outputs.

    sub DESTROY { my $self = shift; &stop; }

    That should be

    sub DESTROY { my $self = shift; $self->stop(); }

    stop expects to be called as a method.

    local $SIG{INT} = 'IGNORE'; kill INT => -$$;

    That should be

    kill INT => $self->{pid};

    There's no reason to kill every child.

      When I change my function as below and send "sleep 50" as a command, can_write is returning input file handle as one of the value. so it means it can be written ?. I expected it should not return input file handle since we cannot write into 'sleep' command.

      Any Explanation or fix??

      sub canSendInput { my $self = shift; my $pid = waitpid($self->{pid}, WNOHANG) my @ready = $self->{'selector'}->can_write(1);# Dumper(\@ready); }

      Thanks,
      Thiagu

        I expected it should not return input file handle since we cannot write into 'sleep' command.

        You don't write "into a sleep command". You write into a pipe. As long as the pipe isn't full, it'll report that it can accept data.

        Perhaps you want Expect?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://759379]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (1)
As of 2022-07-02 04:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My most frequent journeys are powered by:









    Results (102 votes). Check out past polls.

    Notices?