1

Perl、Python、Bash などのインタープリター言語なら何でも受け入れますが、私が学ぼうとしているのは Perl です。次のようなタイムスタンプのリストがあります。

17:31:16
17:31:16
17:31:18
17:31:29

2 つの連続する行の間の最大間隔 (上位 5 つ) をすべて検索し、タイム スタンプと行番号を返したいと考えています。基本的に、これはソフトウェア ビルドのログ ファイルであり、どの手順が最も長くかかったかを判断しようとしています。私が示した例は実際にはすでにフィルタリングされており、行は実際には [15:57:42]: CC net/sunrpc/xprtsock.o のようになっています。時間差が最も大きかった行番号。

これは、ログからタイムスタンプを分離するために使用したものです

   perl -lane 'print $1 if $_ =~ /^\[(\d+:\d+:\d+)\]:*/'

私が達成したい出力のタイプは次のようなものです:

 line 574 20:04:54
 line 575 20:24:55
 Difference 00:20:01

問題を解決したくない場合は、疑似コードを参照するか、アドバイスをいただければ幸いです。私は時間を費やしましたが、表示する有用なコードはありません。

4

2 に答える 2

2

時間のコンポーネントを個別にキャプチャするために、時間一致の正規表現を少しアップグレードします。ビルドが真夜中前に開始され、翌日の早朝まで実行されることを心配する必要はありますか?

#!/usr/bin/env perl
use strict;
use warnings;

my $oldtime = "";  # hh:mm:ss for end of long interval
my $oldlineno = 0; # line number in the file of second line
my $oldoffset = 0; # offset in seconds from midnight of second command
my $olddiff = 0;   # time taken for longest command

sub hhmmss
{
    my($time) = @_;
    my(@tm) = (int($time/3600), int($time/60)%60, $time%60);
    return @tm;
}

while (<>)
{
    chomp;
    next unless m/^((\d\d):(\d\d):(\d\d))\s+/;
    my $newoffset = (($2 * 60) + $3) * 60 + $4;
    if ($oldoffset == 0)
    {
        $oldtime = $1;
        $olddiff = 0;
        $oldoffset = $newoffset;
        $oldlineno = $.;
    }
    elsif (($newoffset - $oldoffset) > $olddiff)
    {
        $oldtime = $1;
        $olddiff = $newoffset - $oldoffset;
        $oldoffset = $newoffset;
        $oldlineno = $.;
    }
}

if ($oldoffset != 0)
{
    my $prvlineno = $oldlineno - 1;
    my $newoffset = $oldoffset - $olddiff;
    my(@tm) = hhmmss($newoffset);
    printf "line $prvlineno: %.2d:%.2d:%.2d\n", $tm[0], $tm[1], $tm[2];
    print  "line $oldlineno: $oldtime\n";
    @tm = hhmmss($olddiff);
    printf "diff:   %.2d:%.2d:%.2d\n", $tm[0], $tm[1], $tm[2];
}

与えられたデータ ファイル ( data) と上記のスクリプト ( dt.pl):

17:31:16 line1
17:31:18 line2
17:31:29 line3
17:33:59 line4
18:00:21 line5
18:21:03 line6
18:41:25 line7
19:51:54 line8
19:52:34 line9

以下のスクリプトレットは、次の出力を生成します。

$ for i in $(seq 1 9); do sed ${i}q data | perl dt.pl; done | so
line 0: 17:31:16
line 1: 17:31:16
diff:   00:00:00
line 1: 17:31:16
line 2: 17:31:18
diff:   00:00:02
line 2: 17:31:18
line 3: 17:31:29
diff:   00:00:11
line 3: 17:31:29
line 4: 17:33:59
diff:   00:02:30
line 4: 17:33:59
line 5: 18:00:21
diff:   00:26:22
line 4: 17:33:59
line 5: 18:00:21
diff:   00:26:22
line 6: 18:00:21
line 7: 18:41:25
diff:   00:41:04
line 7: 18:41:25
line 8: 19:51:54
diff:   01:10:29
line 7: 18:41:25
line 8: 19:51:54
diff:   01:10:29
$

コードを作成する前に、この問題についてどのように考えていたかを知りたいです。

これは明らかに、以前の行情報 (の関連部分) の記録を保持して、現在の行との差異を計算する必要があるという問題でした。また、現在の最大差を保持する必要があります。これは、2 つ目の一致する行を読み取るまで正式には確立できません。これが設計を推進します。3 つの値を無条件に割り当て、4 番目の値 ( $olddiff) を条件付きで割り当てることでゼロに減らすことができる、コード内の大きな繰り返し。その後は、主にメカニズムと戦術の問題です。

このように複数の行にまたがって照合するのは厄介なプロセスです。適切な状態を維持することに対処する必要があります。部分的には、経験の問題です。この種のことを数十回行った後は、次回はそれほど時間がかかりません.

于 2013-09-24T20:13:41.777 に答える
1

最大の違いを取得するだけです

perl -l -n -e 'BEGIN {$m=0;$last=0;$am=$.;} /(\d+):(\d+):(\d+)/; $v=($1*3600)+($2*60)+$3;   if ($last && $v-$last > $m) { $am=$.; $m=$v-$last;} $last=$v; END { print "max diff ",$m, " at line $am\n" }' d.txt

トップ5

perl -l -n -e 'BEGIN {%h=();$last=0;} /(\d+):(\d+):(\d+)/; $v=($1*3600)+($2*60)+$3;   if ($last) { $h{$v-$last}=$.;} $last=$v; END { for ((sort {$b <=> $a} keys %h)[0..4]) { print "line ",$h{$_}," $_"; }}' d.txt

パイソンで

last = 0
list = []
linenumber = 1
for t in (open("d.txt","r").readlines()):
    q=3600
    v=0
    for x in t.split(":"):
        v = v + q*int(x)
        q = q / 60
    if (last >0):
      list.append([v-last, linenumber])
    last = v
    linenumber = linenumber + 1

top = sorted(list, key=lambda n: n[0], reverse=True)[0:5]
print top
于 2013-09-24T20:00:33.310 に答える