1

Perl を使用して、外部プログラム (シェル コマンド) の出力を 1 行ずつ解析したいと考えています。コマンドは継続的に実行されるため、スレッドに配置し、共有変数を使用してメイン ルーチンと通信します。

今まで私のコードは次のように見えます

#!/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;すると解決策になる可能性がありますが、「現在選択されている出力チャネル」に対してのみです。私の文脈でそれをどのように使用しますか?

前もって感謝します。

4

2 に答える 2

0

一般に、スクリプトは子プロセスの出力のバッファリングを変更できません。いくつかの特定のケースでは、適切なスイッチで開始することでこれを実行できる場合がありますが、それだけです。

独自のコードを記述して実行と読み取りを行う代わりに、スクリプトを書き直してIPC::Runモジュールを使用することをお勧めします。まさにこの種の問題を解決するために存在します。ドキュメンテーションはこれまでで最高ではありませんが、モジュール自体は十分にテストされ、しっかりしています。

于 2014-09-24T08:52:14.893 に答える
0

ikegamiCalle 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);
于 2014-09-24T14:41:18.187 に答える