【发布时间】:2014-11-17 20:37:21
【问题描述】:
我想使用 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; 可能是一种解决方案,但仅适用于“当前选择的输出通道”。我将如何在我的上下文中使用它?
提前谢谢你。
【问题讨论】:
-
“长时间运行的命令”的输出没有被刷新。有一个名为
unbuffer的实用程序可以欺骗在连接到终端时使用行缓冲约定的程序。 -
有没有办法控制命令的刷新行为?但是该命令每 100 毫秒打印一次到“stdout”。 shell stdout 和管道到 perl 的区别在哪里?
-
我会考虑从命令行运行长时间运行的命令并通过管道传输到
perl -n script。 -
@ikegami 那么将
$cmd = "<long running command> |";更改为$cmd = "unbuffer <long running command> |";应该可以吗? -
是的。您也可以自己创建伪 tty。 IPC::Run 可以轻松做到这一点
标签: perl parsing buffer stdout autoflush