awkを使用して同じファイルの行パターンを比較する方法

awkを使用して同じファイルの行パターンを比較する方法

まず、私は今学び始めて、awkこれを完了するために挑戦したいbashので、完全な答えを見つけるのではなく、解決策ではなく、あちこちでいくつかのヒントとこれを行う方法を探しています。

デフォルトでは、このような大きなログファイルを取得し、次のように構成する必要があります。

  • ユーザーがログインし、パスワードを変更し、ユーザーは同じ秒以内にログアウトします(3つのタスクはすべて1秒以内に完了する必要があります)。
  • これらのタスク(ログイン、パスワードの変更、ログアウト)は、その間に他のタスクなしで順番に発生します。

したがって、私の出力は、上記のテストと一致するユーザーのプロファイル名のみを表示する必要があります。

fxsciaqulmlk
erdsfsdfsdf
fxsciaqulmla

ここにログファイルの一部があります。

Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged in| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user changed password| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged off| -
Mon, 22 Aug 2016 13:15:42 +0200|178.57.66.225|faaaaaa11111| - |user logged in| -
Mon, 22 Aug 2016 13:15:40 +0200|178.57.66.215|terdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:49 +0200|178.57.66.215|terdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:49 +0200|178.57.66.215|terdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user logged in| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user changed password| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user changed profile| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user logged off| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged in| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user changed password| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged off| -
Mon, 22 Aug 2016 13:20:42 +0200|178.57.67.225|faaaa0a11111| - |user logged in| -

これが私が付いているところです。

#!/bin/bash

LIMIT="3"
LOG_FILE="${1}"


if [[ ! -e "${LOG_FILE}" ]]; then
  echo "Cannot open log file: ${LOG_FILE}" >&2
  exit 1
else
  grep 'changed password' -B1 -A1 ${LOG_FILE} \
  | awk '{print $5"\t"$6"\t"$9" "$10}' \
  | awk 'BEGIN{FS="|"; OFS="\t"} {print $1,$3,$4}' \
  | cut -d "    " -f1,3,4,5

....

fi

私のロジックは次のとおりです。 「ログイン」の後と「ログアウト」の前の行に「変更されたパスワード」文字列があることを確認したいと思います。これら2つの項目が一致したら、これらの操作が同じ秒以内に完了したかどうかを比較したいと思います。

私のロジックが良いかどうか、awkこれを行うには何を使うべきかを教えてください。私も学びたいのですが、何か説明していただければ本当にありがとうございます。

答え1

ファイルに3行のウィンドウを保持し、最後のパターンが見つかるたびに現在と最後の2行をテストできます。

BEGIN {
    FS = "|"
    text[2] = "user logged in"
    text[1] = "user changed password"
    text[0] = "user logged off"
}
    
$5 == text[0] && action[1] == text[1] && action[2] == text[2] &&
$3 == user[1] && $1 == time[1] && $3 == user[2] && $1 == time[2] {
    print $3
}
    
{
    time[2] = time[1]; user[2] = user[1]; action[2] = action[1]
    time[1] = $1; user[1] = $3; action[1] = $5
}

使用法:

$ awk -f tst.awk file
fxsciaqulmlk
erdsfsdfsdf
fxsciaqulmla
  • このBEGINグループは、最初の行を解析する前に実行され、FSフィールド区切り文字です。

  • 2番目のグループは条件付きで印刷ユーザーと一致します。スパゲッティに似ていますが動作します。awk最初のエラーが見つかった場合は、各セクションの評価が停止されるため、順序が重要です。したがって、まずその行に「ログアウト」が含まれているかどうかをテストし、最初の2行に別のタスクが含まれているかどうかをテストします。

  • 最後のグループは3行のウィンドウを予約するために使用されます。ハッシュのある変数は[1]最後の行を意味し、ハッシュのある変数は[2]2番目から最後の行を意味します。

  • 注:初期化されていないすべての変数は、空の文字列(または数値0、型が緩い)として扱われます。また、awk配列は連想配列であり、1ここでは2ハッシュ値です。


最初の数行について、さらに異なるテストをしたい場合は、スケルトンは次のようになります。

condition_for_last_row {
    for (i=1;i<=2;i++) {
        n = split(prev[i],arr)
        # do comparisons here, arr[1] to arr[n] are
        # the fields of the i-th previous row
    }
}

{
    prev[2] = prev[1]
    prev[1] = $0
}

答え2

awkの代わりにPerlを使用することは、主にstr2time()Date :: Parseモジュールの機能を容易にするためのものです。

$ perl -MDate::Parse -F'\|' -lane '
  if    (/user logged in/)        { $event{$F[2]}{login}  = str2time($F[0]) }

  elsif (/user changed password/) { $event{$F[2]}{passwd} = str2time($F[0]) }

  elsif (/user logged off/ &&
         defined($event{$F[2]}{login})  && 
         defined($event{$F[2]}{passwd}) &&
         (str2time($F[0]) - $event{$F[2]}{login} <= 1)) { print $F[2]; delete $event{$F[2]} }

  else { delete $event{$F[2]} }' input.log 
fxsciaqulmlk
erdsfsdfsdf
fxsciaqulmla

このPerlワンライナーは日付::分析モジュール時間の日付コレクションは日付フィールドをtime_t値(1970年1月1日真夜中の新起源以降の秒)に変換します。

このオプションはperlに各行を印刷せずに入力を繰り返し(sオプション-nと同様)、各入力行を自動的に配列に分割するように指示します(andオプションを使用すると、perlが各行を自動的に分割するawkと同様に動作します)。 )$ 1、$ 2、$ 3などの行を入力します。)このオプションは、Perlに入力と出力の行末の改行を自動的に処理するように指示します。sed-n@F-a-F'\|'-l

スクリプトは、ログインしてパスワードを変更するユーザーと、これらのイベントがハッシュ(連想配列)で発生したタイミングを追跡します%events。 %eventsは実際にはHash-of-Hashesです(HoH、詳細についてはPerl Data Structures Cookbookを参照してください。参考資料を参照man perldsc)。ここで、各要素(ユーザー名で入力)は別のハッシュ(またはでlogin入力passwd、タイムスタンプが値)です。

特定のユーザーの2つのイベントが確認された後、同じユーザーがログインしてから1秒以内にログアウトしたことを確認すると、ユーザー名を印刷してそのユーザー名のイベントを削除します。

そのユーザーに関連する他のイベントが見つかった場合は、そのユーザーの現在のイベント履歴をすべて削除します。

1行スクリプトで書かれていますが、スタンドアロンスクリプトに簡単に変換できます。

注:Bashや他の多くの言語の配列と同様に、Perl配列は1ではなく0から始まります。したがって、$F[0]最初のフィールド(日付と時刻)は$F[2]3番目のフィールド(ユーザー名)です。

関連情報