【问题标题】:sed: using variables across multiple linessed:跨多行使用变量
【发布时间】:2010-10-27 02:35:00
【问题描述】:

我正在尝试从 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

基本上,这是跨多个连接的服务器操作日志。我需要分析 admin 用户在 'bind' 操作上花费的时间,但是这个服务器很忙,所以我需要消除很多噪音。

在伪代码中:

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?

【问题讨论】:

    标签: perl shell sed


    【解决方案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)
    

    【讨论】:

    • 谢谢,但在这些连接上还会发生我不感兴趣的其他操作。结果行无法识别结果是用于“绑定”操作的事实。该脚本的第二部分最终会为我提供管理员绑定到的任何连接上发生的所有操作结果。我会把这个添加到问题中。
    • 我并不完全清楚你想要什么。您想要哪些行作为输出?你能提供样本输出吗?根据您的描述“fgrep -v DoSearch”或“egrep(已完成|发送)”到我上面的解决方案的末尾。
    • 这个解决方案让我担心的是“最终的 fgrep 会找到包含一个连接号的所有行”我不想要所有包含连接号的行,我只想要前几行在连接上执行“DoBind”后找到连接。我添加了示例结果。
    • 感谢 python 代码。如果可以的话,我会再次 +1 你。我最终掸掉了我的 PERL 书,然后就这样做了。我一直想学习 python……看到 perl 解决方案有多丑,确实激励了我。
    【解决方案2】:

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

    我不记得这些命令,但它不是很复杂;您只需在参考文档中查找即可。

    【讨论】:

    • 感谢您的领导。我现在正在阅读它,但不确定它如何适用。 “DoBind”和“cn=admin”行不是问题所在。问题是将更改的连接地址传递给下一个搜索模式。你是说我将从匹配行中提取地址字段并将其附加到后面的模式缓冲区?
    • 啊,对不起。我读错了一点。是的,您可以从匹配行中提取一些字段并将其粘贴到保持缓冲区中。然后,您的其他规则将尝试匹配 DoBind 模式,但前提是保持缓冲区不为空。
    • 我认为自己非常精通 sed 和 shell 脚本,而这正是我必须经常查阅参考手册的内容。使用基于行的工具进行多行处理本质上是一种技巧,尽管它可能是完成这项工作的最佳工具,而不是用某种通用语言编写的成熟程序。如果你不能让它在 sed 脚本中工作,你可能会退回到 bash,因为它有内置的正则表达式运算符(或者 perl - 颤抖 - 如果你更舒服的话)。
    【解决方案3】:

    好吧,我找不到单独使用 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;
            }
          }
        }
      }
    }
    

    【讨论】:

      【解决方案4】:

      作为一项智力挑战,我提出了一个使用 sed 的解决方案(根据要求),但我想说使用其他一些技术(我最喜欢的 perl)会更容易理解,因此更容易支持。

      对于 sed 中的多行处理,您有几个选择:

      • 您可以使用 hold space - 可以用来存储全部或部分 pattern space 以供后续处理,或者

      • 您可以使用N 等命令将更多行添加到模式空间

        你可以使用 hold space

      注意:以下示例使用 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
      

      '

      【讨论】:

        猜你喜欢
        • 2014-11-20
        • 1970-01-01
        • 2017-07-10
        • 2011-10-04
        • 2022-01-20
        • 2019-03-24
        • 1970-01-01
        • 1970-01-01
        • 2023-02-10
        相关资源
        最近更新 更多