2010-05-14 45 views
1

提取XML块我有一个看起来像一个日志文件中的以下内容:如何从日志文件在Linux上

2010-05-12 12:23:45 Some sort of log entry 
2010-05-12 01:45:12 Request XML: <RootTag> 
<Element>Value</Element> 
<Element>Another Value</Element> 
</RootTag> 
2010-05-12 01:45:32 Response XML: <ResponseRoot> 
<Element>Value</Element> 
</ResponseRoot> 
2010-05-12 01:45:49 Another log entry 

我想要做的是提取请求和响应XML(最终甩掉他们到他们自己的单个文件中)。我有一个使用egrep的类似解析器,但是XML全部在一行上,而不是像上面那样的多个。

日志文件也有点大,打日志500-600兆。较小的日志,我会通过PHP脚本读入并使用正则表达式匹配,但是如此庞大的文件所需的内存量可能会导致脚本不可用。

有没有一种简单的方法使用Linux机器上的内置工具(在这种情况下是CentOS)来提取多行文件,或者我将不得不硬着头皮用Perl或PHP读取整个文件提取它?

+0

真的没有理由不能用PHP来完成。当然,你不能将整个日志加载到内存中,你必须以流的方式对其进行扫描。 – Artefacto 2010-05-14 02:01:20

回答

2
# Example usage: 
# perl script.pl data.xml RootTag > RootTag.xml 

use strict; 
use warnings; 

my $tag = pop; 

while (<>){ 
    if (s/.*(<$tag>)/$1/ .. s/(<(\/)$tag>).*/$1/){ 
     print; 
     last if $2; 
    } 
} 

有关flip-flop operator的详细信息,请参阅文档。

+0

漂亮,整洁,简单。如果有多个XML集,我将它扩展为将每个报表转储到一个文件,但这足以让我开始。 – dragonmantank 2010-05-14 03:35:44

+0

在你的情况下,你可能需要写$ tag = qr /(?: RootTag | ResponseRoot)/; qr创建一个正则表达式,而(?:...)意味着括号不会捕获匹配,也不是必须的,但使用它们可能是一个好习惯。此外,这里的假设是,这2个标签仅用作其各自XML片段的根(可能)。 – mirod 2010-05-14 07:48:54

1

你的问题意味着你不正确的想法;如果有一种方法可以用一种语言来完成你所要求的功能,那么你可以用任何语言来完成。

没有理由将整个日志读入内存。您只需逐行阅读并提取您想要的信息。你只需要保持一个状态(不在标签中,在RootTag内部,在ResponseRoot中等等),并按照你的意愿处理数据。

+0

我不是说我不想用适当的语言去做,我只是知道有时候你可以用内置的工具拼凑一些好东西:)如果不可能,那么我会编码 – dragonmantank 2010-05-14 02:47:47

2

听起来像是sed工作(我是如此很想说SuperSed ;-)

sed -n '/^<.\+>/H; /\(Request\|Response\) XML/{s/^.*</</;x;p}; ${x;p}' xmllog 

其中xmllog是您的日志文件的名称。在开始时你会得到一个空白行,但可以用egrep '.+'甚至只是tail -n +2过滤掉。

作为解释,sed是由一系列匹配条件和相应动作组成的程序的一个小解释程序。 sed按行(因此命名为“流编辑器” - >“sed”)遍历文件,并且对于每行,对于与行上文本匹配的程序中的每个条件,它会应用相应的操作。在这种情况下:

/^<.\+>/ 

是匹配包含<后跟任何字符的任何行正则表达式条件(.)重复一次或更多次(\+),随后> - 基本上与XML标签的任何行。相关的操作是H,它将行附加到“保持缓冲区”。另一个条件是

/\(Request\|Response\) XML/ 

其中,当然,是,要么RequestResponse后跟一个空格,然后XML匹配的正则表达式。相应的动作是

{s/^.*</</;x;p} 

其中第一确实线(^)的开头的取代(s),随后重复任意随后<倍(*)任何字符(.),只有< 。基本上,这是摆脱线上的第一个XML标签之前的任何东西。然后它将刚刚读取的行与“保留缓冲区”(其中包含上一条日志消息的XML)切换(x)并打印(p)刚从保持缓冲区中交换的内容。最后,

$ 

输入结束比赛,并再次{x;p}只是交换保持缓冲区的内容进入“打印缓冲区”,然后打印它。

您可以更改命令,以满足您的需求,例如,如果你需要的东西来分隔不同的记录,这就会把它们之间的空行:

sed -n '/^<.\+>/H; /\(Request\|Response\) XML/{s/^.*</\n</;x;p}; ${x;p}' xmllog 

(在这种情况下,当然,请不要使用egrep来过滤开始处的空白行)。

+0

真的,真的很有创造力。我需要学习更多的sed和awk命令。 – dragonmantank 2010-05-14 03:36:17

+0

实际上,我花了一点时间阅读手册,将它们合在一起......但sed和awk是至少要熟悉的方便的东西。 (FWIW我的第一个想法是Perl) – 2010-05-14 06:26:38