Parsing 从一行打印以x开头的某些单词

Parsing 从一行打印以x开头的某些单词,parsing,awk,syslog,Parsing,Awk,Syslog,我想从这行/var/log/syslog打印以srcip和srcintf开头的单词 Jul 21 13:13:35 some-name date=2020-07-21 time=13:13:34 devname="devicename" devid="deviceid" logid="0000000013" type="traffic" subtype="forward" level="

我想从这行/var/log/syslog打印以srcip和srcintf开头的单词

Jul 21 13:13:35 some-name date=2020-07-21 time=13:13:34 devname="devicename" devid="deviceid" logid="0000000013" type="traffic" subtype="forward" level="notice" vd="root" eventtime=1595330014 srcip=1.2.3.4 srcport=57324 srcintf="someinterface" srcintfrole="wan" dstip=5.6.7.8 dstport=80 dstintf="anotherinterface" dstintfrole="lan" sessionid=supersecretid proto=6 action="deny" policyid=0 policytype="policy" service="HTTP" dstcountry="Sweden" srccountry="Sweden" trandisp="noop" duration=0 sentbyte=0 rcvdbyte=0 sentpkt=0 appcat="unscanned" crscore=30 craction=131072 crlevel="high"
看起来像这样的东西

date=2020-07-21 time=13:13:34 devname="devicename" action="deny" policyid=0 srcintf="someinterface" dstintf="anotherinterface" srcip=1.2.3.4 srcport=57324 -----> dstip=5.6.7.8 dstport=80
目前我正在使用awk来做这件事。it的可扩展性非常差,原因很明显:

cat /var/log/syslog | awk '{print $5,$6,$7,$25,$26,$17,$21,$15,$16,"-----> "$19,$20}'
此外,并非所有行都在同一“字段”中具有srcip。所以有些线是倾斜的


或者系统日志消息重写器更适合于此目的?你将如何着手解决这个问题?提前谢谢

我为您提供了一个awk答案,它非常灵活,而且比简单的一行程序更具编程性。日志文件中的行通常如下所示:

key1=value1 key2=value2 key3=value3 ...
此awk中的想法是将其分解为awk中的一个关联数组,以便将元素称为:

a[key1]=>value1 a[key2]=>value2 ... a[key2,"full"]=>key2=value2 ...
使用中介绍的函数,您可以编写:

awk '
    function str2map(str,fs1,fs2,map,   n,tmp) {
       n=split(str,map,fs1)
       for (;n>0;n--) { 
         split(map[n],tmp,fs2);
         map[tmp[1]]=tmp[2]; map[tmp[1],"full"]=map[n]
         delete map[n]
       }
    }
    { str2map($0," ","=",a) }
    { print a["date","full"],a["time","full"],a["devname","full"],a["action","full"] }
   ' file
这种方法非常灵活。在行的顺序或其他方面也没有依赖关系


注意:上述方法不考虑报价。因此,如果空格出现在带引号的字符串中,它可能会把事情搞砸。

如果您有
filter.awk

BEGIN{
   split(filter,a,",");
   for (i in a){
      f[a[i]]=1;
   }
}
{
   for (i=1; i<=NF; i++) {
      split($i,b,"=");
      if (b[1] in f){
         printf("%s ", $i);
      }
   }
   printf("\n");
}
在您指定的过滤器中,关键字以逗号分隔。它必须找到

注意:此脚本还假设文件的格式为:
key1=value key2=value
,并且值中没有空格

$ cat tst.awk
{
    delete f
    for (i=5; i<=NF; i++) {
        split($i,tmp,/=/)
        f[tmp[1]] = $i
    }

    print f["date"], f["time"], f["devname"], f["action"], f["policyid"], f["srcintf"], \
         f["dstintf"], f["srcip"], f["srcport"], "----->", f["dstip"], f["dstport"]
}

上面假设引用的字符串不包含示例输入中所示的空格。

split()的第三个参数是一个regexp,而不是字符串,因此您应该尝试使用regexp而不是字符串分隔符,以防有朝一日受到影响
split(filter,a,/,/)
split($i,b,/=/)
来自:“第三个参数fieldpat是一个描述字符串中字段的regexp(就像FPAT是一个描述输入记录中字段的regexp)。它可以是一个regexp常量,也可以是一个字符串。”是的,真的,它以“或字符串”结尾,这是正确的,您可以在需要时在那里指定一个字符串,但是awk首先将该字符串转换为regexp,然后
split()
使用它,因此当您提供一个字符串时,它会被awk解析两次,这意味着您会遇到一些额外的复杂性,例如必须在转义时加倍,因此只有在需要时才会这样做,就像把引号从shell变量中去掉一样-你可以在需要的时候这样做。顺便说一句,同样的问题只是在一个不同的问题中用不同的函数出现了-请参阅。@kvantour我添加了
删除f
,谢谢。我不认为这篇文章需要它,但它不会有什么坏处,其他阅读这篇文章的人可能会从中受益。
$ cat tst.awk
{
    delete f
    for (i=5; i<=NF; i++) {
        split($i,tmp,/=/)
        f[tmp[1]] = $i
    }

    print f["date"], f["time"], f["devname"], f["action"], f["policyid"], f["srcintf"], \
         f["dstintf"], f["srcip"], f["srcport"], "----->", f["dstip"], f["dstport"]
}
$ awk -f tst.awk file
date=2020-07-21 time=13:13:34 devname="devicename" action="deny" policyid=0 srcintf="someinterface" dstintf="anotherinterface" srcip=1.2.3.4 srcport=57324 -----> dstip=5.6.7.8 dstport=80