在linux中解析日志文本

时间:2015-12-23 19:13:34

标签: linux text-parsing

我需要从日志中选择一个文本并将该字段存储在新文件中。

例如,下面是日志格式

[Mon Dec 07] [error] [client 10.0.0.65] [id "981004"] [file "sample"] [line "84"] [hostname "test"] [uri "/login"] [unique_id "VmVddAo"]
[Mon Dec 07] [error] [client 10.0.0.65] [file "sample"] [line "47"] [id "960015"] [rev "1"] [msg "Request Missing an Accept Header"] [severity "NOTICE"][ver "OWASP_CRS/2.2.9"] [maturity "9"] [accuracy "9"] [tag "MISSING_HEADER_ACCEPT"] [tag "WASCTC/WASC-21"] [tag "OWASP_TOP_10/A7"] [tag "PCI/6.5.10"] [hostname "test"] [uri "/home"] [unique_id "VmVddQo"]

想要打印输出,如下所示

[Mon Dec 07] [id "981004"] [uri "/login"]
[Mon Dec 07] [id "960015"] [uri "/home"]

我使用awk以列方式打印

grep "Mon Dec 07" filename | sed '/\[[a-zA-Z]/\t&/g' | awk -F'\t' '{print $5}'

But i got the below output 

[id "981004"]
[file "sample"]

Because the column are found on different places, for example

[id "981004"] in the 4th column
[id "960015"] in the 6th column 

如何使用like获取值,id作为键,双引号内部是该键的值。选择所有值后,必须将其作为列存储在新文件(csv)中。

感谢vrs& MirosławZalewski

#!/bin/bash

search=$1
log=$2
regexp="s/(\[$search[^]]*\]).+(\[id[^]]*\]).+(\[uri[^]]*\]).+/\1 \2 \3/p"
sed -rn "$regexp" $2

&安培;

perl -n -e '$,=" "; @groups = $_ =~ m/(\[.*?\]).*(\[(?:id|uri).*?\]).*((?-2)).*/ ; print @groups, "\n"' /path/to/log/file.log

都工作了......

2 个答案:

答案 0 :(得分:1)

您可以使用以下脚本执行此操作:

#!/bin/bash

search=$1
log=$2
regexp="s/(\[$search[^]]*\]).+(\[id[^]]*\]).+(\[uri[^]]*\]).+/\1 \2 \3/p"
sed -rn "$regexp" $2

您可以将此程序保存到文件(例如script.sh),使其可执行(chmod +x script.sh)并运行$ ./script.sh "Mon Dec 07" log.txt

这是脚本的作用:

  1. 将脚本的第一个参数分配给变量$search(要匹配行的文本),将第二个参数分配给变量$log(日志文件的名称)
  2. sed创建正则表达式
    • (...)表示分组
    • \[some text\]表示方括号内的some text(使用baclslashes进行转义)
    • [^...]表示除...之外的任何字符,即[^]]表示任何字符,但结束方括号(正则表达式终止所需)
    • .+表示任何字符的正数
    • \1表示我们需要使用第一组中的文字(请参阅第一个项目符号)
    • sed的选项-rn意味着分别禁止每行的默认打印和扩展正则表达式的使用
  3. 在日志文件log.txt
  4. 上使用带有sed的正则表达式

    希望有所帮助。

答案 1 :(得分:1)

这是perl中快速而肮脏的单行解决方案:

perl -n -e '$,=" "; @groups = $_ =~ m/(\[.*?\]).*(\[(?:id|uri).*?\]).*((?-2)).*/ ; print @groups, "\n"' /path/to/log/file.log

这假设第一个字段包含日期。它不需要iduri字段的任何特定顺序,但会按照它们出现在文件中的顺序打印出来。

perl中的另一个更灵活,更少脏和多线的解决方案:

%seeked = map { $_ => 1 } qw(id uri unique_id severity msg);

while (<>) {
    my $string = $_;
    my $closing = 1;
    while ( $closing != -1 ) {
            $closing = index($string, "]");
            $field = substr($string, 0, $closing+1);
            $field =~ s/^\s+|\s+$//g;
            $string = substr($string, $closing + 1);

            my @content = split(/ /, $field, 4);

            if (scalar @content == 3 and $field !~ m/"/) {
                    print $field . " ";
                    next;
            }

            if ($seeked{ substr($content[0], 1) }) {
                    print $field . " ";
            }
    }
    print "\n";
}

要使用它,请复制该代码,将其粘贴到文件中并将其另存为whatever.pl。然后,在shell中键入:

perl /path/to/whatever.pl /path/to/log/file.log

在第一行代码中,在qw之后的括号内,声明要打印的字段。如果您想要其他字段,或者您不想要其中某些字段,请修改该行。

此解决方案仍然无法按预先指定的顺序打印字段,并且无法打印某些字符串来代替缺少可选字段。你需要一个解析器 - 首先在某些数据结构中放置字段,然后在行尾打印检测到的字段。

此外,“日期识别”代码是Very Wrong™。它检查字段是否包含两个空格,并且它不包含双引号字符。如果它匹配这两个条件,则假定该字段为日期并打印它。正确的解决方案将检查字符串是否代表有效日期。这可以通过Date::Manip CPAN来完成 模块,但需要单独安装。

如果要以任意顺序打印任意字段,则需要为此文件格式编写自定义(?)解析器。这不应该太难,但有一些陷阱需要寻找:

  • 日期字段具有隐式密钥
  • 类型字段具有隐式密钥
  • 至少“客户”字段不引用值
  • 许多字段是可选的
  • 某些字段可以多次出现(至少“标记”)

我认为CSV不适合这种数据格式。它最适合二维数据结构。因为您的数据具有存储列表的可选字段和字段,所以更灵活的东西(如XML或JSON)会更好。

另一方面,似乎编写这些日志的人认为阅读它们很容易。我建议检查供应商文档/支持,看看他们是否有任何特定的解决方案。