如何从日志文件中提取多个以逗号分隔的字符串?

如何从日志文件中提取多个以逗号分隔的字符串?

我需要从日志文件中提取某些字符串,尽管这些字符串不在标准化字段/列中。例如:

date="2017-01-03 08:30:02 -0500",fac=f_kernel_ipfilter,area=a_general_area,type=t_attack,pri=p_major,hostname=hostname,category=policy_violation,event="ACL deny",attackip=1.1.1.1,attackzone=internal,app_risk=low,app_categories=remote-admin,netsessid=c550e586ba75a,src_geo=US,srcip=1.1.1.1,srcport=38256,srczone=internal,protocol=6,dst_geo=US,dstip=2.2.2.2,dstport=80,dstzone=external,rule_name=Deny_All,reason="Traffic denied by policy.",application=SSH

我想获取 srcip、srczone、protocol、dstip、dstzone、dstport 和rule_name。我目前使用 perl 惰性匹配来删除我不需要的 OUT 字段。有没有办法只获取这 8 个字符串和逗号内的数据,,dstport=80,无论日志文件中的位置如何?同一数据有许多不同的条目位置,这使得这变得很困难。

答案1

这是一种快速但肮脏的方法,使用perl

$ perl -F, -lane '@l = grep {/srcip|srczone|protocol|dstip|dstzone|dstport|rule_name/} @F; 
                  print join ",",@l' file 
srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstport=80,dstzone=external,rule_name=Deny_All

-a使得 perl 的行为类似于 awk,并根据 给定的字符分割其输入行-F,并将它们保存为数组的元素@F。然后,我们grep创建数组并在数组中保留与目标单词匹配的元素@l,最后@l使用逗号进行打印连接。

请注意,如果您的任何模式可以是子模式(假设您有foo=barfoobar=baz),则此操作将会失败。

对于较长的目标模式列表(假设您不想编写实际的脚本),您可以将它们存储在数组中并将它们连接起来|以创建 grep 的正则表达式。通过\b在每个模式周围添加,您也可以防止匹配子模式。如果我们还删除不必要的临时数组,我们会得到:

$ perl -F, -lane '
    BEGIN{
     $pat="\\b" . join "\\b|",qw(srcip= srczone= protocol= dstip= dstzone= dstport= rule_name=)
    } print join ",",grep {/$pat/}@F' file 
srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstport=80,dstzone=external,rule_name=Deny_All

我们的常驻专家表示,无论 sed awk 或 [. 。 ..] 你的命令

抱歉,但这显然是荒谬的。以下是在每个工具中执行此操作的一种(多种)方法:

  1. Bourne(再次)外壳。不要使用这个,我只是展示它来证明它是可能的。

    $ pat=(srcip= srczone= protocol= dstip= dstzone= dstport= rule_name=); 
    $ o=""; while IFS=, read -a fields; do 
                for f in "${fields[@]}"; do 
                    for pat in "${pat[@]}"; do 
                        [[ $f =~ $pat ]] && o="$f,$o"
                    done 
                done
               done < file ; echo ${o%,}
    
  2. awk

    将目标模式保存在文件中:

    $ cat patterns
    srcip
    srczone
    protocol
    dstip
    dstzone
    dstport
    rule_name
    

    然后:

    $ awk -F, '(NR==FNR){ 
                    pat[$0]++; 
                    next;
                } 
                {
                    for(i=1;i<=NF;i++){ 
                        split($i,a,"="); 
                        if(a[1] in pat){
                            printf "%s=%s,",a[1],a[2]
                        }
                    }
                    print ""
                }' patterns file | sed 's/,$//'
    srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstport=80,dstzone=external,rule_name=Deny_All
    
  3. sed(和外壳)

    $ pat=(srcip= srczone= protocol= dstip= dstzone= dstport= rule_name=);
    $ for p in ${pat[@]}; do 
        sed -E "s/.*($p[^,]*).*/\1/" file; done | 
            sed ':a;N;$!ba;s/\n/,/g'
    srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstzone=external,dstport=80,rule_name=Deny_All
    
  4. Bourne shell(或任何 POSIX shell)+ sed(至于 1.,不要这样做,这是可能的,但很愚蠢)

    $ set srcip= srczone= protocol= dstip= dstzone= dstport= rule_name=
    $ for f in "$@"; do sed "s/.*\($f[^,]*\).*/\1/" file; done | sed ':a;N;$!ba;s/\n/,/g'
    srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstzone=external,dstport=80,rule_name=Deny_All
    

答案2

GNU 的解决方案awk

gawk -v OFS= -v FPAT=',(srcip|srczone|protocol|dstip|dstzone|dstport|rule_name)=[^,]*' -e 'NF > 0 { $1=$1; print }'

在这里,我使用了 GNU 特有的功能awk:对于FPAT变量,我使用正则表达式指定字段的格式,以便与正则表达式匹配的行的每个部分都分配给$1...$n.然后我分配$1$1,以便$0仅使用$1...$n.

答案3

我对此有点延迟,但会提供一个建议 - 这种数据非常适合map哈希:

#!/usr/bin/env perl

use strict;
use warnings;
#for debugging - can be removed;
use Data::Dumper;

my @fields = qw ( srcip srczone protocol dstip dstzone dstport rule_name );

#read STDIN or files specified on command line (just like grep/sed/awk)
while ( <> ) {

   #split commas
   #then read key-value pairs. 
   my %row = map { m/(.*)=(.*)/ } split /,/;
   #for debugging:
   print Dumper \%row;

   #print fields tab-separated and in order as above. 
   print join "\t", @row{@fields};
}

oneliner-ify 稍微困难一点,因为你有一个字段列表需要拼写。但:

perl -lane -F, 'BEGIN { @k = qw ( srcip srczone protocol dstip dstzone dstport rule_name ) } %r = map { m/(.*)=(.*)/ } @F; print join "\t", @r{@k}'

答案4

巴什

IFS=, read -r -a fields <<< "$date"
results=()
for keyval in "${fields[@]}"; do 
    IFS='=' read -r key value <<< "$keyval"
    case $key in 
        srcip|srczone|protocol|dstip|dstzone|dstport|rule_name) results+=("$keyval")
    esac
done
(IFS=,; echo "${results[*]}")
srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstport=80,dstzone=external,rule_name=Deny_All

相关内容