Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Reading non-blockingly / "awk has to be better for something."

by Anonymous Monk
on Jul 28, 2012 at 07:42 UTC ( #984156=perlquestion: print w/ replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hail, monks.

I'm writing a GUI frontend to a command-line program, and am in need of a progress bar. The command-line program outputs things like this:

Initialising...\n Progress: 0%\r Progress: 1%\r Progress: 2%\r Progress: 3%\r

I have an I/O helper that can tell me when there is something waiting in the I/O buffer for me. It then calls my callback:

sub io_cb { my $fh = shift; local $/ = "\r"; my $line = <$fh>; # blocks until there is a \r # update progress bar with $line }

However, the kicker is that the command-line program's initialisation takes a while. Therefore, the sub will block the few seconds until the first percentage line is output, which leads to the UI freezing. ($line will then contain Initialising...\nProgress: 0%\r -- one line more than I hoped for)

It would be great if I could set $/ to qr/[\r\n]/, but the documentation tells me only awk supports that. Is there a way I could just read whatever is immediately available on $fh?

Comment on Reading non-blockingly / "awk has to be better for something."
Select or Download Code
Re: Reading non-blockingly / "awk has to be better for something."
by Grimy (Pilgrim) on Jul 28, 2012 at 08:37 UTC
    my $line = ''; my $chr; $line .= $chr until (($chr = getc($fh)) =~ /[\r\n]/);
    Not tested, but it might work, or at least give you an idea.
Re: Reading non-blockingly / "awk has to be better for something."
by james2vegas (Chaplain) on Jul 28, 2012 at 08:49 UTC
    Yes there is, set your $fh non-blocking (IO::Handle's blocking method) and use IO::Handle's getline method on $fh, bearing in mind that getline will return undef (ending a while loop) when there isn't a line to read (<$fh> would also work non-blockingly, so getline is not strictly necessary)

    As for the other issue, just split your returned line on "\n".
Re: Reading non-blockingly / "awk has to be better for something."
by Anonymous Monk on Jul 28, 2012 at 08:59 UTC

    Throwing in more detail, the I/O poller module is Gtk2::Helper, whose documentation warns: "you should not use Perl's builtin read and write functions here because these operate always with buffered I/O. Use low level sysread() and syswrite() instead."

    I'm not really sure what to construe of that. sysread() needs a size -- should I just read one byte at a time as Grimy suggested above? Am I doing this all wrong by using Perl's buffered I/O? (The code behaves as expected even if I use my current style.)

      When using Gtk2 I normally use the AnyEvent wrapper over that, especially for its AnyEvent::Handle's methods which let you define reading by line as you wish. But keeping with plain Gtk2 you can just, with your $fh set non-blocking, call sysread with the maximum expected length of your data, like this:

      use IO::Handle; my $fh = *STDIN; $fh->blocking(0); while (1) { # this block would go in your callback, not in a loop lik +e this: $fh->sysread( my $data, 255 ); print "$data"; # split $data into lines on CR|LF put the first lin +e on the end of the last line of the previous block (use an array per +haps) }

        I'm having a bit of trouble going the plain Gtk2 route. I'm not sure what's wrong since I mucked around with the code and changed its behaviour a few times already, but it either 1) works until EOF, after which it goes to an infinite loop, or 2) sysreads the whole output on one go.

        my $cmd = [perl => -e => '$|++; for my $i (0..4) { $sum+= $i; print +"Line $i: sum = $sum\r\n"; sleep 1;}']; my $cb = sub {print "CB: >>" . shift() . "<<\n";} open($fh, '-|', @$cmd) or die "failed to launch external command: $! +"; $fh->blocking(0); $tag = Gtk2::Helper->add_watch($fh->fileno, in => sub { if ($fh->eof) { print "pipe EOF\n"; Gtk2::Helper->remove_watch($tag); close $fh; return; } while (1) { my $buf; $fh->sysread($buf, 4095); print "READ: >>$buf<<\n"; last unless $buf; for (split(/[\r\n]/, $buf)) { $cb->($_); } } # keep watch active return TRUE; });

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2015-07-04 07:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (58 votes), past polls