如何在非固定位置使用 grep 或 awk 字符串



我没有找到类似的解决方案,除了解析多个参数,它本身就是一个完整的脚本。
我需要从脚本的 nftables 获取句柄 #,并且遇到了问题,因为它并不总是位于同一位置。 使用Iptables,它是行号,总是列在第一个字段中,因此很容易捕获插入位置。 当我这样做时:nft -n -a list chain inet fw4 forward输出将是这样的:

table inet fw4 {
chain forward { # handle 2
type filter hook forward priority 0; policy drop;
ip saddr 192.168.0.0/16 ip daddr 192.168.0.0/16 accept # handle 223
ct state 0x2,0x4 accept comment "!fw4: Allow forwarded established and related flows" # handle 179
iifname "br-lan" jump forward_lan comment "!fw4: Handle lan IPv4/IPv6 forward traffic" # handle 180
iifname "eth0.2" jump forward_wan comment "!fw4: Handle wan IPv4/IPv6 forward traffic" # handle 181
jump handle_reject # handle 182
}
}

我需要捕获"句柄#",在本例中为 223,前后没有任何空格 通常我会这样做:

InsNo=$(nft -n -a list chain inet fw4 forward|grep -m 1 "192.168.0.0"|awk '{print $10}')

仅当存在固定数量的字段时才有效。 我也试过

InsNo=$(nft -n -a list chain inet fw4 forward|grep -m 1 "192.168.0.0"|awk -F'handle ' '{print $2}')

如果句柄编号后没有更多参数,则有效。 但是我想知道是否有更可靠的方法,在获取这个数字时,前后也没有空格。

awk

解决方案是你在这里最简单的选择(以及你问到的),但可以通过匹配handle和连续数字来编写sed可能"更安全"的版本。只是一个额外的选择,因为...为什么不呢。

nft -n -a list chain inet fw4 forward |
sed -En '/192.168.0.0/s/.*# +handle +([0-9]+).*/1/p'

最终解决方案是"只有第一场比赛",结果如下:

nft -n -a list chain inet fw4 forward |
sed -En '/192.168.0.0/{s/.*# +handle +([0-9]+).*/1/p; q}

()参数是一个捕获组,稍后由1恢复,即返回匹配项。[0-9]匹配任何数字,以下+匹配"一个或多个"= 任意数量的连续数字。p打印线到标准输出。它是必需的,因为我们使用 -n 标志抑制所有行。要只打印一个匹配项,只需将替换项封装在{}大括号中,并添加q退出。

您可以检查该行是否包含192.168.0.0,然后如果手柄部分可以在该行中的任何位置,则获得匹配项。

在第一次匹配后,使用 substr 删除前导handle并退出程序。

例如,如果示例数据处于file

awk '
/192.168.0.0/ && match($0, /handle [0-9]+/) {
print substr($0, RSTART+7)
exit  
}' file

输出

223

如果对于 exampe,句柄部分总是在 ip 之后,您还可以将gnu awk与捕获组一起使用:

awk '
match($0, /192.168.0.0.*handle ([0-9]+)/, a) {
print a[1]
exit  
}' file

您显然缺少的信息是awk有一个名为NF的变量表示字段数。而且,您应该使用-Fgrep 选项将192.168.0.0模式解释为固定字符串,而不是正则表达式。

InsNo=$(nft -n -a list chain inet fw4 forward | grep -F -m 1 "192.168.0.0" |
awk '{print $NF}')

但是在awk中配管grep通常是一种浪费:

InsNo=$(nft -n -a list chain inet fw4 forward |
awk '/192.168.0.0/ {print $NF}')

如果您感兴趣的字段并不总是最后一行,而是始终遵循字段#handle,并且始终是十进制数,我们可以更准确一点:

InsNo=$(nft -n -a list chain inet fw4 forward |
awk '/192.168.0.0/ {
for(i=1; i<=NF-2; i++)
if($i=="#" && $(i+1)=="handle" && $(i+2)~/^[0-9]+$/)
print $(i+2)
}')

最后,如果您只想要第一个匹配项,只需在print之后添加一个exit语句:

InsNo=$(nft -n -a list chain inet fw4 forward |
awk '/192.168.0.0/ {
for(i=1; i<=NF-2; i++)
if($i=="#" && $(i+1)=="handle" && $(i+2)~/^[0-9]+$/) {
print $(i+2); exit
}
}')

最新更新