【问题标题】:Perl (tk): how to run asynchronously a system command, being able to react to it's output?Perl(tk):如何异步运行系统命令,能够对其输出做出反应?
【发布时间】:2013-07-19 14:18:56
【问题描述】:

我正在使用 Perl “Tk” 为外部命令(“sox”,如果有帮助的话)编写一个包装器。 当然,我需要异步运行它,以避免阻塞 tk 的 MainLoop()。 但是,我需要阅读它的输出来通知用户命令的进度。

我正在使用 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)

当然,while 循环会阻塞 MainLoop,直到 $cmd 确实终止。

有什么方法可以异步读取输出句柄吗? 还是我应该使用标准叉子? 该解决方案也应该在win32下工作。

【问题讨论】:

    标签: perl perltk


    【解决方案1】:

    对于文件句柄的非阻塞读取,请查看Tk::fileevent

    这是一个示例脚本,如何一起使用管道、分叉进程和文件事件:

    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;
    

    分叉在 Windows 下可能工作也可能不工作。在 Tk 内分叉时也需要谨慎;你必须确保两个进程中只有一个在做 X11/GUI 的东西,否则会发生不好的事情(X11 错误、崩溃......)。一个好的方法是在创建 Tk MainWindow 之前进行 fork。

    【讨论】:

    • Tk::IO 似乎将此功能包装在一个不错的包中;它的文档暗示了很多破损,但我一直在使用它的黑客版本和 Expect 来控制进程一段时间(哇,自 2005 年以来!)。是不是有什么地方不适合使用?
    • 我倾向于忘记Tk::IO——如果它适合你,那就没问题!其实它内部也是使用fileevent,管道是使用open "-|"创建的,系统命令是使用exec执行的,所以这里不用担心遇到X11错误。
    • 太棒了!我不知道 Tk::fileevent... 我可以强制我丑陋的代码在 while 循环中插入“$mw->update”调用,但这绝对是一个更干净的解决方案,我会尽快测试跨度>
    猜你喜欢
    • 2010-12-17
    • 2014-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-12
    • 1970-01-01
    相关资源
    最近更新 更多