1

LDAP ログ ファイルから特定のユーザーのバインドを「grep」しようとしています。必要な行は、ログ内の複数の行に分散されます。入力例は次のとおりです。

[2009/04/28 17:04:42.414] DoBind on connection 0x7c8affc0
[2009/04/28 17:04:42.414] Bind name:cn=admin,ou=appids,o=admineq, version:3, authentication:simple
[2009/04/28 17:04:42.415] Failed to authenticate local on connection 0x6cc8ee80, err = log account expired (-220)
[2009/04/28 17:04:42.416] Sending operation result 53:"":"NDS error: log account expired (-220)" to connection 0x6cc8ee80
[2009/04/28 17:04:42.416] Operation 0x3:0x60 on connection 0x6cc8ee80 completed in 3 seconds
[2009/04/28 17:04:42.416] Sending operation result 0:"":"" to connection 0x7c8affc0
[2009/04/28 17:04:42.416] Operation 0x1:0x60 on connection 0x7c8affc0 completed in 0 seconds
[2009/04/28 17:04:48.772] DoSearch on connection 0x7c8affc0
[2009/04/28 17:04:48.772] Search request:
base: "o=intranet"
scope:2  dereference:0  sizelimit:0  timelimit:600  attrsonly:0
filter: "(guid='03ADmin)"
attribute: "cn"
attribute: "cn"
attribute: "cn"
attribute: "cn"
attribute: "objectClass"
attribute: "guid"
attribute: "mail"
[2009/04/28 17:04:48.773] Sending operation result 0:"":"" to connection 0x7c8affc0
[2009/04/28 17:04:48.773] Operation 0xe851:0x63 on connection 0x7c8affc0 completed in 0 seconds

この例では、結果は次のようになります。

[2009/04/28 17:04:42.414] DoBind on connection 0x7c8affc0
[2009/04/28 17:04:42.414] Bind name:cn=admin,ou=appids,o=admineq, version:3, authentication:simple
[2009/04/28 17:04:42.416] Sending operation result 0:"":"" to connection 0x7c8affc0
[2009/04/28 17:04:42.416] Operation 0x1:0x60 on connection 0x7c8affc0 completed in 0 seconds

基本的に、これは複数の接続にわたるサーバー操作のログです。管理ユーザーによる「バインド」操作に費やされた時間を分析する必要がありますが、このサーバーは非常にビジーであるため、多くのノイズを除去する必要があります。

擬似コード:

for each line in file
    if line contains "DoBind" and next line contains "cn=admin"
        print both lines
        find the connection number X in lines
        skip lines until "Sending operation result.*to connection X" is found
        print two lines

この例では、ユーザー「cn=admin」が前にある「DoBind」行と、接続番号「0x7c8affc0」に従ってリストされている結果行を取得したいと思います。別の接続で行われている「認証に失敗しました」メッセージなど、バインドの開始と終了の間で必要のない他の操作が行われる場合があります。

さらに、バインドが完了した後、接続で他の操作が行われますが、これには興味がありません。上記では、「バインド」後に発生した DoSearch 操作の結果をキャプチャしてはなりません。

私は「sed」でこれをやろうとしていますが、これは仕事に適したツールのようです。残念ながら、私は初心者であり、これは学習経験です。これが私がこれまでに持っているものです:

/.*DoBind on connection \(0x[0-9a-f]*\)\n.*Bind name:cn=OblixAppId.*/ p
/.*Sending operation result.*to connection \1\nOperation.*on connection \1 completed.*/ p

sed は、'\1' を使用している 2 行目に文句を言います。接続アドレスをキャプチャし、その後の検索でそれを使用して結果文字列をキャプチャしようとしていますが、明らかに正しく使用していません。「#」変数は、各検索操作に対してローカルであるようです。

ある検索から別の検索に「変数」を渡す方法はありますか、それとも代わりに perl を学習する必要がありますか?

4

4 に答える 4

2

知的な課題として、sed を使用した解決策を思いつきました (要求に応じて) が、他のテクノロジ (私のお気に入りの perl) を使用する方が理解しやすく、サポートしやすいと思います。

sed での複数行処理に関しては、いくつかのオプションがあります。

  • ホールド スペースを使用できます。これは、後続の処理のためにパターン スペースのすべてまたは一部を保存するために使用できます。

  • のようなコマンドを使用して、パターン スペースにさらに行を追加できますN

    保留スペースを使用できます

注:以下の例では GNU sed を使用しています。さらに、マルチコマンド構文 (';' を に置き換えます) を変更することで、Solaris sed で動作するようにすることもできます。スクリプトをよりコンパクトにするために、GNU sed バリエーションを使用しました。

以下のスクリプトは、読者私の利益のためにコメントされています。

sed -n '
# if we see the line "DoBind" then store the pattern in the hold space
/DoBind/ h

# if we see the line "cn=admin", append the pattern to the holdspace
# and branch to dobind
/cn=admin/{H;b dobind}

# if we see the pattern "Sending...." append the hold space to the
# pattern and  branch to doop
/Sending operation result/{G;b doop}

# branch to the end of the script
b

# we have just seen a cn=admin, ad the hold space contains the last
# two lines
:dobind

# swap hold space with pattern space
x

# print out the pattern space
p

# strip off everying that is not the connection identifier
s/^.*connection //
s/\n.*$//

# put it in the hold space
x

# branch to end of script.
b

# have just seen "Sending operation" and the current stored connection
#identifier has been appended to the pattern space
:doop

# does the connection id on both lines match? Yes do to gotop.
/connection \(0x[0-9a-f]*\).*\n\1$/ b gotop

# branch to end of script
b

# pattern contains two lines "Sending....", and the connection id.
:gotop

# delete the second line
s/\n.*$//

# read the next line and append it to the pattern space.
N

# print it out
p

# clear the pattern space, and put it into the hold space - hence
# clearing the hold space
s/^.*$//
x

'

于 2009-05-19T13:32:15.473 に答える
1

1回のパスで参照したい場合は、sed参照を詳しく調べたいと思うでしょう-確かにそれを行うことができます。ホールド バッファーとパターン バッファーを交換する sed コマンドを調べて、2 つを比較します。「cn=admin」に一致するマルチステップ ルールを記述し、それをホールド バッファにスワップし、ホールド バッファが空でない場合に「DoBind」パターンに一致させることができます。

コマンドをすぐに覚えることはできませんが、それほど複雑ではありません。参照ドキュメントで調べる必要があります。

于 2009-05-18T18:07:42.203 に答える
1
fgrep -B1 cn=admin logfile | 
sed -n 's/.*DoBind on connection \(.*\)/\1/p' | 
fgrep -wf - logfile

この最初の fgrep は Bind 行と前の行 (-B1) を抽出し、sed は接続番号を引き出し、最後の fgrep は接続番号の 1 つを含むすべての行を見つけます。

これは 2 パス ソリューションです。1 パスも可能ですが、実装がより複雑になります。

編集:これは、Pythonで必要なことを行うソリューションです。ただし、異なる接続間のインターリーブされたログ行を正しく処理しないため、これは完全に正しいわけではないことに注意してください。修正するのに十分気をつけている場合は、あなたに任せます. また、少し効率が悪く、必要以上に正規表現のコンパイルと照合を行います。

import re

todo = set()
display_next = False
previous_dobind = None

for line in open('logfile'):
  line = line.strip()
  if display_next:
    print line
    display_next = False
    continue
  dobind = re.search('DoBind on connection (.*)', line)
  bind = re.search('Bind name:cn=admin', line)
  oper = re.search('Sending operation result.*to connection (.*)', line)
  if dobind:
    previous_dobind = (dobind.groups(1), line)
  elif previous_dobind:
    if bind:
      todo.add(previous_dobind[0])
      print previous_dobind[1]
      print line
    previous_dobind = None
  elif oper:
    conn = oper.groups(1)
    if conn in todo:
      print line
      display_next = True
      todo.remove(conn)
于 2009-05-18T17:54:38.627 に答える
0

うーん、sedだけでは解決に至りませんでした。私の醜いperlソリューションは次のとおりです。

open INFILE, $ARGV[0] or die "Couldn't open file $ARGV[0]";
while (<INFILE>) {
  if (/(.*DoBind on connection (0x[0-9a-f]*))/) {
    $potentialmatch = $1; $connid = $2;
    $currentline = <INFILE>;
    if ($currentline =~ /(.*Bind name:cn=OblixAppId.*)/) {
      print $potentialmatch . "\n" . $1 . "\n";
      $offset = tell INFILE;
      while($currentline = <INFILE>) {
        if ($currentline =~ /(.*Sending operation result.*to connection $connid.*)/) {
          print "$1\n";
          next;
        }
        if ($currentline =~ /(.*Operation.*on connection $connid completed.*)/) {
          print  "$1\n";
          seek INFILE, $offset, 0;
          last;
        }
      }
    }
  }
}
于 2009-05-19T00:32:33.783 に答える