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' 的第二行。我正在尝试捕获连接地址并在后续搜索中使用它来捕获结果字符串,但我显然没有正确使用它。'#' 变量似乎是每个搜索操作的本地变量。

有没有办法将“变量”从一个搜索传递到另一个搜索,或者我应该学习 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
fgrep -B1 cn=admin logfile | 
sed -n 's/.*DoBind on connection \(.*\)/\1/p' | 
fgrep -wf - logfile

第一个 fgrep 提取 Bind 行和前一行 (-B1),sed 提取连接号,最后一个 fgrep 查找包含一个连接号的所有行。

这是一个两次通过的解决方案,一次通过是可能的,但实施起来更复杂。

编辑:这是一个在 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 回答
1

如果您希望一次通过 sed 参考,您将需要仔细查看它 - 您当然可以这样做。查看交换保持和模式缓冲区的 sed 命令,并比较两者。您可以编写一个匹配“cn=admin”的多步规则,并将其交换到保持缓冲区,然后在保持缓冲区不为空时匹配“DoBind”模式。

我不记得这些命令,但它并不是非常复杂;您只需要在参考文档中查找它。

于 2009-05-18T18:07:42.203 回答
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 回答