私の質問は、改行を待つ代わりに入力をすぐに処理するにはどうすればよいですかというアンチテーゼです。増大するログ ファイルの読み取りを続行したいが、ファイルが指定された秒数の間増大しなかった場合に停止します。
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 バイトのバッファを使用していますが、そのような長すぎる行にはドットが出力されます。私の目的には十分です。