使用Perl解析stdout时避免缓冲

时间:2014-09-23 15:03:39

标签: perl parsing buffer stdout autoflush

我想使用Perl逐行解析外部程序(某些shell命令)的输出。该命令连续运行,因此我将其放入一个线程并使用共享变量与我的主程序进行通信。

到目前为止,我的代码与

类似
#!/usr/bin/perl

use warnings;
use strict;
use threads;
use threads::shared;

my $var :shared; $var="";

threads->create(
    sub {
        # command writes to stdout each ~100ms
        my $cmd = "<long running command> |";
        open(README, $cmd) or die "Can't run program: $!\n";
        while(<README>) {
            my $line = $_;
            # extract some information from line
            $var = <some value>;
            print "Debug\n";
        }
        close(README);
    }
);

while(1) {
    # evaluate variable each ~second
    print "$var\n";
    sleep 1;
}

对于某些命令,这种方法非常正常,并且线条在它们进入时进行处理。输出类似于:

...
Debug
Debug
...
<value 1>
...
Debug
Debug
...
<value 2>
...

然而,对于其他命令,这表现得很奇怪,并且正在逐块处理这些行。因此,$var无法更新,并且Debug也不会打印一段时间。然后,突然输出(类似于):

...
<value 1>
<value 1>
<value 1>
...
Debug
Debug
Debug
...
<value 20>

$var设置为最后/当前值。然后重复。解析总是被延迟并在块中完成,而$var之间没有更新。

首先:除了使用管道之外,有没有更好的/适当的方法来解析外部程序的输出(逐行!)?

如果没有,我该如何避免这种行为?

我已经读过,使用autoflush(1);$|=1;可能是一种解决方案,但仅适用于&#34;当前选择的输出频道&#34; 。我将如何在我的上下文中使用它?

提前谢谢。

2 个答案:

答案 0 :(得分:0)

在一般情况下,您的脚本无法更改子进程的缓冲区。输出。在某些特定情况下,您可以通过使用适当的开关启动它,但这是关于它的。

我建议您不要编写自己的代码来执行运行和阅读,而是重新编写脚本以使用IPC::Run模块。它的存在是为了解决这类问题。文档并不是最好的,但模块本身经过了充分的测试和稳固。

答案 1 :(得分:0)

感谢 ikegami Calle Dybedahl 我找到了以下问题的解决方案:

#!/usr/bin/perl

use warnings;
use strict;
use threads;
use threads::shared;
use sigtrap qw(handler exit_safely normal-signals stack-trace error-signals);
use IPC::Run qw(finish pump start);

# define shared variable
my $var :shared; $var="";

# define long running command
my @cmd = ('<long running command>','with','arguments');
my $in = '';
my $out = '';
# start harness
my $h = start \@cmd, '<pty<', \$in, '>pty>', \$out;

# create thread
my $thr = threads->create(
    sub {
        while (1) {
            # pump harness
            $h->pump;
            # extract some information from $out
            $var = <some value>;
            # empty output
            $out = '';
        }
    }
);

while(1) {
    # evaluate variable each ~second
    print "$var\n";
    sleep 1;
}

sub exit_safely {
    my ($sig) = @_;
    print "Caught SIG $sig\n";
    # harness has to be killed, otherwise
    # it will continue to run in background
    $h->kill_kill;
    $thr->join();
    exit(0);
}

exit(0);