如何从Linux上的日志文件中提取XML块

时间:2010-05-14 01:55:59

标签: php regex linux perl command-line

我有一个如下所示的日志文件:

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来读取整个文件中的内容提取它?

3 个答案:

答案 0 :(得分: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的详细信息,请参阅文档。

答案 1 :(得分: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过滤掉开头的空白行。)

答案 2 :(得分:1)

你的问题暗示你没有正确思考;如果有办法用一种语言(有)做你所要求的......那么你可以用任何语言来做。

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