テキストファイルから統計を印刷する

テキストファイルから統計を印刷する

以下のテキストファイル(events.dat)があります(抜粋部分のみが表示されます)。

RepID12 01/01/2010 20:56:00 S10
RepID12 01/01/2010 20:56:00 S03
RepID20 01/01/2010 20:56:00 S17
RepID33 01/01/2010 20:56:00 S02
RepID33 01/01/2010 20:56:00 S18
RepID38 01/01/2010 20:56:00 S11
RepID39 01/01/2010 20:56:00 S20
RepID26 02/01/2010 01:39:00 S20
RepID29 02/01/2010 01:39:00 S16
RepID29 02/01/2010 01:39:00 S03
RepID22 02/01/2010 01:39:09 S01
RepID26 02/01/2010 01:39:09 S02
RepID40 02/01/2010 01:39:18 S02
RepID38 02/01/2010 01:39:09 S05
RepID31 02/01/2010 01:39:09 S06
RepID31 02/01/2010 01:39:09 S08
RepID09 02/01/2010 01:39:09 S09
RepID23 02/01/2010 01:39:18 S09
RepID19 02/01/2010 01:40:09 S09
RepID21 02/01/2010 01:40:18 S09
RepID28 02/01/2010 01:40:27 S09
RepID43 02/01/2010 01:40:09 S14

など、合計48時間の時間がかかります。毎分60を超えるイベントが見つかった場合にのみ行を印刷したいと思います。

たとえば、次のコマンドを使用すると、1分間に発生したイベントの数を計算できます。

grep "02/01/2010 01:39" events.dat | wc -l

たとえば、60 が返されます。これは、1 分あたりの最大イベント数です。

同じ操作を実行しながら、合計48時間毎分確認し、1分あたり60以上のイベントが見つかった行のみを印刷するにはどうすればよいですか?事前にありがとう

答え1

理想的には、ファイルを一度だけ処理し、メモリにできるだけ少なく保存することをお勧めします。では、awk次のことができます。

awk -v n=60 '
  {
    t = $2 substr($3, 1, 5);
    if (t == last_t) {
      if (++lines > n)
        print
      else
        if (lines == n)
          print saved $0
        else
          saved = saved $0 RS
    } else {
      saved = $0 RS
      lines = 1
      last_t = t
    }
  }' < your-file

このアプローチのいくつかの利点は次のとおりです。

  • これはストリーム処理指向です。入力は到着するとすぐに処理され、出力はできるだけ早く放出されます(ライン60が表示されている場合)。これにより、リアルタイム出力を後処理できます(と同様tail -fn +1 log_file)。
  • コマンド()を一度だけ実行するので、awkできるだけ効率的です。反対の極端はループ内で複数のコマンドを実行する。シェルスクリプトで最も高価な作業は、通常、コマンドを分岐して実行することです。最適化とは、このような状況をできるだけ最小化することを意味します。
  • 最大60行だけがメモリに格納されるため、メモリ使用量が制限されます(行自体のサイズが制限されていると仮定)。
  • awkコードは非常に明確で説明が必要です。サイズが重要な場合は、サイズを小さくして次のように1行に入れることもできます。

    awk '{t=$2substr($3,1,5);if(t==l){if(++i>n)print;else if(i==n)print s$0;else s=s$0RS}else{s=$0RS;i=1;l=t}}' n=60 file
    

答え2

これは最も効率的なソリューションではありませんが、最初に1分あたりのイベント数を計算してから、数が60より大きい場合は、分ごとにファイルをgrepできます。

sort -k 2,3 your_log_file \
| uniq -c -s 8 -w 16 \
| while read count _ date time _; do
    [ "$count" -ge 60 ] && grep -F " $date ${time%:*}" your_log_file
done

メモ:

  • 上記の基本例では、最初にファイルを時系列でソートします。
  • これが興味のある唯一の情報である場合、最初の2行は1分あたりのイベント数を提供します。

ファイルがイベントでいっぱいになると、grepそのファイルに対して多くのタスクを実行する可能性が高くなります。より良い解決策は、ログファイルを順番に読み、最後の行を覚えておくことです。次の分に達したときにこの行の数が60を超える場合は、その行を印刷します。そのような解決策については、Stéphaneの答えを参照してください。

答え3

このようにして、利用可能な時間(分)を分離できます。

root@debian:# awk -F" " '{print $2" "$3}' b.txt |cut -f1-2 -d: |uniq
01/01/2010 20:56
02/01/2010 01:39
02/01/2010 01:40
02/01/2010 20:56

その後、この値を使用して配列を割り当てることができます。

修正されたコード:

readarray -t stamps < <(awk -F" " '{print $2" "$3}' b.txt |cut -f1-2 -d: |uniq)
for stamp in "${stamps[@]}";do
ev=$(grep "$stamp" b.txt |wc -l)
echo "In $stamp found $ev events "
#if [ "$ev" -gt 60 ]; then
# do the stuff
#fi
done

出力:

In 01/01/2010 20:56 found 7 events 
In 02/01/2010 01:39 found 11 events 
In 02/01/2010 01:40 found 4 events 
In 02/01/2010 20:56 found 7 events 

答え4

awk '{ print $2 " " $3 }' < input \
| cut -c1-16                      \
| sort                            \
| uniq -c                         \
| awk '{ if ($1 > 60) print $2 }'

つまり、日付と時刻のフィールドを取得し、秒を削除し、結果を並べ替え(注:日付がISO形式の場合はうまく機能します)、各固有の日付/時刻の組み合わせの数を見つけて、Count> 60使用して印刷します。

関連情報