4

私の質問は、改行を待つ代わりに入力をすぐに処理するにはどうすればよいですかというアンチテーゼです。増大するログ ファイルの読み取りを続行したいが、ファイルが指定された秒数の間増大しなかった場合に停止します。

CPAN でSys::AlarmCallを見つけ、以下のように試しましたが、実行してもタイムアウトしません。

perl progress.tracker.pl progress.tracker.pl

<>これは、' ' 演算子に関連付けられた自動マジックと関係があると推測しています。しかし、コードを書き直す方法がわかりません。(任意の数のファイルではなく) 1 つのファイルのみを明示的に開いても問題ありません。ファイルが指定されていない場合は標準入力がデフォルトになります。1 つのファイル名でのみ使用することを期待しています。

(このスクリプトは、読み取った行ごとにドットを生成し、読み取った 50 行ごとに改行を生成し、25 行のドットごとにタイムスタンプを出力します。私はこれを使用して、長時間実行ビルドの進行状況を追跡します。現在の具体化は によって供給されますtail -fが、これは、主に、現在存在しないプログレス トラッカーに書き込むための入力を取得しないためです.「最後の」行は、私が通常処理するログ ファイルのマーカーです.削除したいです. . タイムアウトは、1 秒未満ではなく、数分のオーダーになります。)

#!/usr/perl/v5.10.0/bin/perl -w
#
# @(#)$Id: progress.tracker.pl,v 1.3 2009/01/09 17:32:45 jleffler Exp jleffler $
#
# Track progress of a log-generating process by printing one dot per line read.

use strict;
use constant DOTS_PER_LINE => 50;
use constant LINES_PER_BREAK => 25;
use constant debug => 0;
use POSIX qw( strftime );
use Sys::AlarmCall;

sub read_line
{
    print "-->> read_line()\n" if debug;
    my $line = <STDIN>;
    printf "<<-- read_line(): %s", (defined $line) ? $line : "\n" if debug;
    return $line;
}

my $line_no = 0;
my $timeout = 30;
my $line;

$| = 1;     # Unbuffered output

while ($line = alarm_call($timeout, 'read_line', undef))
{
    $line_no++;
    print ".";
    print "\n" if ($line_no % DOTS_PER_LINE == 0);
    printf "%s\n", strftime("%Y-%m-%d %H:%M:%S", localtime(time))
        if ($line_no % (DOTS_PER_LINE * LINES_PER_BREAK) == 0);
    last if $line =~ m/^Trace run finished: /;
}

print "\n";
print $line if defined $line && $line =~ m/^Trace run finished: /;

助言がありますか?(できれば、「**** をやめて C でコーディングする」ことは別として!)


File::Tailは、私の要件をかなり満たしているようです。改訂されたコードは次のとおりです。

#!/usr/perl/v5.10.0/bin/perl -w
#
# @(#)$Id: progress.tracker.pl,v 3.2 2009/01/14 07:17:04 jleffler Exp $
#
# Track progress of a log-generating process by printing one dot per line read.

use strict;
use POSIX qw( strftime );
use File::Tail;

use constant DOTS_PER_LINE   => 50;
use constant LINES_PER_BREAK => 25;
use constant MAX_TIMEOUTS    => 10;
use constant TIMEOUT_LENGTH  => 30; # Seconds

my $timeout    = TIMEOUT_LENGTH;
my $line_no    = 0;
my $n_timeouts = 0;
my $line;

sub print_item
{
    my($item) = @_;
    $line_no++;
    print "$item";
    print "\n" if ($line_no % DOTS_PER_LINE == 0);
    printf "%s\n", strftime("%Y-%m-%d %H:%M:%S", localtime(time))
        if ($line_no % (DOTS_PER_LINE * LINES_PER_BREAK) == 0);
}

$| = 1;     # Unbuffered output

# The foreach and while loops are cribbed from File::Tail POD.
my @files;
foreach my $file (@ARGV)
{
    push(@files, File::Tail->new(name=>"$file", tail => -1, interval => 2));
}

while (1)
{
    my ($nfound, $timeleft, @pending) = File::Tail::select(undef, undef, undef, $timeout, @files);
    unless ($nfound)
    {
        # timeout - do something else here, if you need to
        last if ++$n_timeouts > MAX_TIMEOUTS;
        print_item "@";
    }
    else
    {
        $n_timeouts = 0;  # New data arriving - reset timeouts
        foreach my $tail (@pending)
        {
            # Read one line of the file
            $line = $tail->read;
            print_item ".";
        }
    }
}

print "\n";
print $line if defined $line && $line =~ m/^Trace run finished: /;

改善の余地があります。特に、タイムアウトは構成可能であるべきです。しかし、それは私が望んでいたように動作するようです。より多くの実験と微調整が必​​要です。

$tail->read() 関数は一度に 1 行ずつ読み取るようです。これは、POD から完全には明らかではありません。


悲しいことに、さらに実際に使用すると、File::Tail コードの使用方法が必要な方法で機能しないように見えます。特に、ファイルで停止すると、再び再開することはないようです。何が問題だったのかを突き止めるのに時間を費やすのではなく、C で自分でコーディングするという別の方法に戻りました。2 時間もかかりませんでした。File::Tail のデバッグ (私の使用) を除けば、これらを Perl にすばやく取り込むことができたかどうかはわかりません。1 つの奇妙な点: 4096 バイトのバッファーを使用するようにコードを設定しました。監視しているビルド プロセスで 1 行の長さが 5000 バイトを超えていることがわかりました。ああ、コードはまだ 4096 バイトのバッファを使用していますが、そのような長すぎる行にはドットが出力されます。私の目的には十分です。

4

2 に答える 2

6

<STDIN> を強要しようとする代わりに、実際のテーリングを処理するためにFile::Tailを試しましたか?

または、その作品が機能する場合、これはどのように失敗していますか?

于 2009-01-14T00:23:37.230 に答える
3

この問題は、おそらく出力バッファリングに関連しています。詳細な説明が必要な場合は、以下をお読みください。

http://www.pixelbeat.org/programming/stdio_buffering/

私の場合 (RHEL では、成長するファイルでパターンが発生したときにすぐに終了したかっtail -n 0 -f file | grep -m 1 patternた)、提案された LD_PRELOADED ライブラリは役に立たず、Expect パッケージのunbufferユーティリティの単純な使用も役に立ちませんでした。

しかし、ブログ投稿 ( http://www.smop.co.uk/blog/index.php/2006/06/26/tail-f-and-awk/ ) に基づいて、テール起動からのリダイレクト入力を発見しましたサブシェルでトリックを行いました:

grep -m 1 pattern <(tail -n 0 -f file)

しかし、これはそれほど単純ではありませんでした。対話型シェルで作業しているときに、同じコマンドを SSH を使用してリモートで実行すると、通常どおりフリーズします。

ssh login@hostname 'grep -m 1 pattern <(tail -n 0 -f file)'

この場合、 Expectのunbufferユーティリティを使用して、tail の出力を unbuffer する必要があることを発見しました。

ssh login@hostname 'grep -m 1 pattern <(unbuffer -p tail -n 0 -f file)'

これは対話型シェルで使用してはなりません- unbuffer はioctl(raw): I/O error!

于 2010-02-25T13:16:39.337 に答える