in reply to Re: IPC::Open3 and blocking filehandles
in thread IPC::Open3 and blocking filehandles

Hi, well I did some digging and came up with a method from "perldoc -f filehandle". It is from "how to read a byte from a filehandle". I did come up with an error at first telling me that my cdefs.ph file had a bunch syntax errors between ") (". I wonder if this is a bug in 5.8? The lines in question all needed a comma between the ") (" .

A typical line needing the comma is

if(defined (defined(&__cplusplus) ? &__cplusplus : 0) && (defined(&__GNUC_PREREQ) ? &__GNUC_PREREQ : 0), (2,8)) {

Notice the comma I inserted between 0) and (2,8)).

Anyways, after fixing that, the following code does what I was looking for. :-)

#!/usr/bin/perl use warnings; use strict; use IPC::Open3; require 'sys/ioctl.ph'; #interface to "bc" calculator my $pid = open3(\*WRITE, \*READ,\*ERROR,"bc"); #if \*ERROR is false, STDERR is sent to STDOUT my($error,$answer)=('',''); while(1){ print "Enter expression for bc, i.e. 2 + 2\n"; chomp(my $query = <STDIN>); #send query to bc print WRITE "$query\n"; #timing delay needed tp let bc output select(undef,undef,undef,.01); #see which filehandles have output from perldoc -q filehandle my $esize = pack("L", 0); ioctl(\*ERROR, FIONREAD(), $esize) or die "Couldn't call ioctl: $!\n"; $esize = unpack("L", $esize); print "esize-> $esize\n"; my $rsize = pack("L", 0); ioctl(\*READ, FIONREAD(), $rsize) or die "Couldn't call ioctl: $!\n"; $rsize = unpack("L", $rsize); print "rsize-> $rsize\n"; #get the output from bc if($esize > 0){sysread(ERROR,$error,$esize); print "Error-> $error\n"} if($rsize > 0){sysread(READ,$answer,$rsize); print "Output->$query = $ +answer\n"} ($error,$answer,$rsize,$esize)=('','',0,0); }

Replies are listed 'Best First'.
Re: Re: Re: IPC::Open3 and blocking filehandles
by sgifford (Prior) on Oct 18, 2003 at 02:55 UTC
    This is still dependant on bc having its output ready within .01 seconds. If the system were very busy or otherwise bogged down and could't meet this deadline, you'd miss a line of output, and then would be off by a line until the program restarted.
      Thanks for pointing out those flaws, sgifford. I did modify my method, so it didn't need to use the delay with select. It waits in a loop until output is available. This will give multiline output from READ, but now I'm stumped by another problem. It seems the output buffer which bc writes to with IPC::Open3 is limited to 4060 bytes. I've tried alot of things, but no matter what I do, bc stops output at 4060 bytes, but will give full output if run by itself from the command line. Try 123^12345 ; which if run from the commandline will output about 16k, but when run from IPC::Open3 will clip at 4060 bytes.
      #!/usr/bin/perl use warnings; use strict; use IPC::Open3; require 'sys/ioctl.ph'; #interface to "bc" calculator my $pid = open3(\*WRITE, \*READ,\*ERROR,"bc"); #if \*ERROR is false, STDERR is sent to STDOUT while(1){ my($error,$answer,$rsize,$esize)=('','',0,0); print "Enter expression for bc, i.e. 2 + 2\n"; chomp(my $query = <STDIN>); #send query to bc print WRITE "$query\n"; #timing delay needed tp let bc output #select(undef,undef,undef,.01); #this while loop waits and eliminates need for above delay #but is limited to 4060 bytes from bc do { #see which filehandles have output from perldoc -q filehandle $esize = pack("L", 0); ioctl(\*ERROR, FIONREAD(), $esize) or die "Couldn't call ioctl: +$!\n"; $esize = unpack("L", $esize); print "esize-> $esize\n" unless ($esize < 1); $rsize = pack("L", 0); ioctl(\*READ, FIONREAD(), $rsize) or die "Couldn't call ioctl: $ +!\n"; $rsize = unpack("L", $rsize); print "rsize-> $rsize\n" unless ($rsize <1); #get the output from bc if($esize > 0){sysread(ERROR,$error,$esize); print "\e[1;31m ERRO +R-> $error \e[0m \n"} if($rsize > 0){sysread(READ,$answer,$rsize); print "Output->$quer +y = $answer\n"} } until(($esize > 0)or($rsize > 0)); }
        Probably bc is doing multiple write's, each outputting a portion of the number. To get all the chunks and put them back together, you'll need to put the select and read in a loop. The tricky part is knowing when to terminate the loop. You'll have to figure out some way to know when bc's done writing the number. I'm not sure what the exact output format is, but at a quick glance it looks like output ends at a newline that's not preceded by a backslash. That's a little tricky to scan for, especially if the backslash comes on one read and the escaped newline on the next, but I don't see any easier options.