MarcoS
MarcoS

Reputation: 17711

Perl (tk): how to run asynchronously a system command, being able to react to it's output?

I'm writing a wrapper to an external command ("sox", if this can help) with Perl "Tk". I need to run it asynchronously, of course, to avoid blocking tk's MainLoop(). But, I need to read it's output to notify user about command's progress.

I am testing a solution like this one, using IPC::Open3:

{
    $| = 1;
    $pid = open3(gensym, ">&STDERR", \*FH, $cmd) or error("Errore running command \"$cmd\"");
}
while (defined($ch = FH->getc)) {
    notifyUser($ch) if ($ch =~ /$re/);
}
waitpid $pid, 0;
$retval = $? >> 8;
POSIX::close($_) for 3 .. 1024; # close all open handles (arbitrary upper bound)

But of course the while loop blocks MainLoop until $cmd does terminate.

Is there some way to read output handle asynchronously? Or should I go with standard fork stuff? The solution should work under win32, too.

Upvotes: 1

Views: 1032

Answers (1)

Slaven Rezic
Slaven Rezic

Reputation: 4581

For non-blocking read of a filehandle, take a look at Tk::fileevent.

Here's an example script how one can use a pipe, a forked process, and fileevent together:

use strict;
use IO::Pipe;
use Tk;

my $pipe = IO::Pipe->new;
if (!fork) { # Child XXX check for failed forks missing
    $pipe->writer;
    $pipe->autoflush(1);
    for (1..10) {
        print $pipe "something $_\n";
        select undef, undef, undef, 0.2;
    }
    exit;
}
$pipe->reader;

my $mw = tkinit;
my $text;
$mw->Label(-textvariable => \$text)->pack;
$mw->Button(-text => "Button", -command => sub { warn "Still working!" })->pack;
$mw->fileevent($pipe, 'readable', sub {
                   if ($pipe->eof) {
                       warn "EOF reached, closing pipe...";
                       $mw->fileevent($pipe, 'readable', '');
                       return;
                   }
                   warn "pipe is readable...\n";
                   chomp(my $line = <$pipe>);
                   $text = $line;
               });
MainLoop;

Forking may or may not work under Windows. Also one needs to be cautious when forking within Tk; you must make sure that only one of the two processes is doing X11/GUI stuff, otherwise bad things will happen (X11 errors, crashes...). A good approach is to fork before creating the Tk MainWindow.

Upvotes: 3

Related Questions