Answers:
假设这与您先前的问题有关,那么您走错了路。与其尝试拼凑一些可以使大多数时间都满足您需求的脚本,而且每次需要做一点点不同的事情时都需要获取一个完全不同的脚本,而只需创建一个可以解析您的脚本即可将输入文件输入到一个数组(f[]
下面)中,该数组将您的字段名(标签)映射到它们的值,然后您可以对结果进行任何操作,例如,从上一个问题中获得此输入文件:
$ cat file
Feb 3 0:18:51 17.1.1.1 id=firewall sn=qasasdasd "time=""2018-02-03" 22:47:55 "UTC""" fw=111.111.111.111 pri=6 c=2644 m=88 "msg=""Connection" "Opened""" app=2 n=2437 src=12.1.1.11:49894:X0 dst=4.2.2.2:53:X1 dstMac=42:16:1b:af:8e:e1 proto=udp/dns sent=83 "rule=""5" "(LAN->WAN)"""
我们可以编写一个awk脚本,该脚本创建一个由其名称/标签索引的值的数组:
$ cat tst.awk
{
f["hdDate"] = $1 " " $2
f["hdTime"] = $3
f["hdIp"] = $4
sub(/^([^[:space:]]+[[:space:]]+){4}/,"")
while ( match($0,/[^[:space:]]+="?/) ) {
if ( tag != "" ) {
val = substr($0,1,RSTART-1)
gsub(/^[[:space:]]+|("")?[[:space:]]*$/,"",val)
f[tag] = val
}
tag = substr($0,RSTART,RLENGTH-1)
gsub(/^"|="?$/,"",tag)
$0 = substr($0,RSTART+RLENGTH)
}
val = $0
gsub(/^[[:space:]]+|("")?[[:space:]]*$/,"",val)
f[tag] = val
}
并且考虑到您可以对数据进行任何操作,只需通过字段名称进行引用即可,例如,使用GNU awk以便-e
于将脚本与命令行脚本混合在文件中:
$ awk -f tst.awk -e '{for (tag in f) printf "f[%s]=%s\n", tag, f[tag]}' file
f[fw]=111.111.111.111
f[dst]=4.2.2.2:53:X1
f[sn]=qasasdasd
f[hdTime]=0:18:51
f[sent]=83
f[m]=88
f[hdDate]=Feb 3
f[n]=2437
f[app]=2
f[hdIp]=17.1.1.1
f[src]=12.1.1.11:49894:X0
f[c]=2644
f[dstMac]=42:16:1b:af:8e:e1
f[msg]="Connection" "Opened"
f[rule]="5" "(LAN->WAN)"
f[proto]=udp/dns
f[id]=firewall
f[time]="2018-02-03" 22:47:55 "UTC"
f[pri]=6
$ awk -f tst.awk -e '{print f["proto"]}' file
udp/dns
$ awk -f tst.awk -e 'f["proto"] ~ /udp/ {print f["sent"], f["src"]}' file
83 12.1.1.11:49894:X0
perl
可能更易于使用。
awk
和sed
脚本通常更简单,perl
因为它本质上是它们的超集,并具有用于常见任务的其他功能。
s/old/new/g
sed 更复杂的sed脚本,而sed也不是awk,所以我们将其搁置一旁。我完全不同意复杂的awk脚本在perl中更简单。当然,它们可能更简短,但简洁性不是软件的理想属性,简洁是其难得的东西,它们几乎没有真正的好处,而且它们通常更难阅读,这就是人们发布zoitz.com之类的原因的原因。 / archives / 13关于perl,并将其称为只写语言,与awk不同。我仍然希望看到与此等效的perl
使用grep -o
,您将必须完全匹配要提取的内容。由于您不想提取proto=
字符串,因此不应该匹配它。
扩展正则表达式将匹配tcp
或udp
后跟一个斜杠和一些非空的字母数字字符串,它是
(tcp|udp)/[[:alnum:]]+
将此应用于您的数据:
$ grep -E -o '(tcp|udp)/[[:alnum:]]+' file
tcp/http
tcp/https
udp/dns
为了确保我们仅在以字符串开头的行上执行此操作proto=
:
grep '^proto=' file | grep -E -o '(tcp|udp)/[[:alnum:]]+'
使用sed
,删除第=
一个空白字符之前和之后的所有内容:
$ sed 's/^[^=]*=//; s/[[:blank:]].*//' file
tcp/http
tcp/https
udp/dns
为确保只在以string开头的行上执行此操作proto=
,您可以插入与上述相同的预处理步骤grep
,或者可以使用
sed -n '/^proto=/{ s/^[^=]*=//; s/[[:blank:]].*//; p; }' file
在这里,我们使用-n
选项抑制默认输出,然后仅在行匹配时触发替换和行的显式打印^proto=
。
使用awk
,使用默认的字段分隔符,然后拆分第一个字段=
并打印其第二位:
$ awk '{ split($1, a, "="); print a[2] }' file
tcp/http
tcp/https
udp/dns
为确保只在以string开头的行上执行此操作proto=
,您可以插入与上述相同的预处理步骤grep
,或者可以使用
awk '/^proto=/ { split($1, a, "="); print a[2] }' file
如果您使用的是GNU grep(用于该-P
选项),则可以使用:
$ grep -oP 'proto=\K[^ ]*' file
tcp/http
tcp/https
udp/dns
在这里,我们匹配proto=
字符串,以确保提取了正确的列,但是随后将其与\K
标志一起从输出中丢弃。
上面假设列以空格分隔。如果制表符也是有效的分隔符,则可以使用它\S
来匹配非空格字符,因此命令将是:
grep -oP 'proto=\K\S*' file
如果您还想防止proto=
子字符串(例如)的匹配字段出现thisisnotaproto=tcp/https
,您可以使用以下方式添加单词边界\b
:
grep -oP '\bproto=\K\S*' file
grep -oP 'proto=\K\S+'
。该proto=tcp/http
可随后代替空格选项卡,\S
不同于[^ ]
将匹配任何非空格字符。
-o
也是GNUism。-P
只有grep
使用PCRE支持构建的GNU才支持(在构建时是可选的)。
另一种awk
方法:
$ awk -F'[= ]' '/=(tc|ud)p/{print $2}' file
tcp/http
tcp/https
udp/dns
这会将awk的字段分隔符设置为=
或一个空格。然后,如果线路相匹配的=
,那么无论ud
或tc
后跟一个p
,打印第二字段。
另一种sed
方法(不适用于所有版本的sed
,但可以与GNU一起使用sed
):
$ sed -En 's/^proto=(\S+).*/\1/p' file
tcp/http
tcp/https
udp/dns
的-n
意思是“不打印”和-E
能够扩展正则表达式这给我们\S
的“非空白”,+
为“一个或多个”以及用于捕获括号。最后,/p
仅当操作成功后,如果替换操作符匹配,最后at才会使sed打印一行。
还有一个perl:
$ perl -nle '/^proto=(\S+)/ && print $1' file
tcp/http
tcp/https
udp/dns
的-n
意思是“逐行读取输入文件中的行,并申请给出的脚本-e
,以每行”。在-l
增加了一个新行到每个print
呼叫(和从输入移除离开新行)。该脚本本身将打印最长的非空格字符,该字符在a之后找到proto=
。
-E
正在变得越来越轻便,但\S
事实并非如此。[^[:space:]]
是更便携的等效项。
这是另一个很简单的解决方案:
grep -o "[tc,ud]*p\\/.* " INPUTFile.txt | awk '{print $1}'
grep
没有任何匹配项。[tc,ud]\*\\/.*
长相对于一个的任一发生t
,或c
,或,
或u
或d
,随后文字*
字符,则p
和反斜杠。你可能是说grep -Eo '(tc|ud)p/.* ' file | awk '{print $1}'
。不过,如果你使用AWK,你不妨做AWK整个事情:awk -F'[= ]' '/(tc|ud)p/{print $2}' file
。
[tc,ud]p
意思是“之一t
,c
,,
,u
或d
后跟一个p
。所以在这里比赛,只是因为tcp
有cp
和udp
有dp
,但它也将匹配,p
或tp
等。此外,现在你有*
,它会匹配ppp
以及(在*
表示“0或更多”,所以它会匹配,即使它不匹配),你不想要一个字符类(。 [ ]
),你想要的是一组:(tc|ud)
(使用-E
的标志grep
。)此外,.*
使其匹配整行。
\*
率先拿到*
他们的命令显示为*而不是斜体降价。当您将命令放入代码格式时,导致出现在\
之前*
(因此导致命令失败)。在编辑其他人的帖子时,请当心更改帖子外观的方法。
ppp
。当然,你是正确的,它将匹配,p
或 tp
-或uucp
,ttp
,cutp
,ductp
或d,up
。
awk '{print $1}' filename|awk -F "=" '{print $NF}'