我正在尝试从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
我想得到“DoBind”行,其前面是用户“cn = admin”,然后是结果行,在本例中根据连接号“0x7c8affc0”列出。其他操作可能发生在我不需要的绑定的开始和结束之间,例如“无法验证”消息,该消息发生在不同的连接上。
此外,在完成绑定之后,其他操作将在我不感兴趣的连接上发生。在上面,不能捕获'bind'之后发生的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呢?
答案 0 :(得分: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
'
答案 1 :(得分: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)
答案 2 :(得分:1)
如果你想要一次通过,你会想要仔细查看一个sed参考 - 你当然可以这样做。查看交换保持和模式缓冲区的sed命令,并比较两者。您可以编写匹配“cn = admin”的多步规则,并将其交换到保持缓冲区,然后在保持缓冲区不为空时匹配“DoBind”模式。
我不记得这些命令,但它并不是非常复杂;你只需要在参考文档中查找它。
答案 3 :(得分: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;
}
}
}
}
}