我需要从日志中选择一个文本并将该字段存储在新文件中。
例如,下面是日志格式
[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
都工作了......
答案 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
这是脚本的作用:
$search
(要匹配行的文本),将第二个参数分配给变量$log
(日志文件的名称)sed
创建正则表达式
(...)
表示分组\[some text\]
表示方括号内的some text
(使用baclslashes进行转义)[^...]
表示除...
之外的任何字符,即[^]]
表示任何字符,但结束方括号(正则表达式终止所需).+
表示任何字符的正数\1
表示我们需要使用第一组中的文字(请参阅第一个项目符号)sed
的选项-rn
意味着分别禁止每行的默认打印和扩展正则表达式的使用log.txt
希望有所帮助。
答案 1 :(得分:1)
这是perl中快速而肮脏的单行解决方案:
perl -n -e '$,=" "; @groups = $_ =~ m/(\[.*?\]).*(\[(?:id|uri).*?\]).*((?-2)).*/ ; print @groups, "\n"' /path/to/log/file.log
这假设第一个字段包含日期。它不需要id
和uri
字段的任何特定顺序,但会按照它们出现在文件中的顺序打印出来。
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)会更好。
另一方面,似乎编写这些日志的人认为阅读它们很容易。我建议检查供应商文档/支持,看看他们是否有任何特定的解决方案。