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

How to grep or awk string in non fixed position

我没有找到类似的解决方案,除了解析多个参数,它本身就是一个完整的脚本。
我需要从脚本的 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
    }
}

我需要捕获“handle #”,在本例中是 223,前后没有任何空格 通常我会这样做:

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

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

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

如果句柄号后没有更多参数,则该方法有效。但是我想知道有没有更靠谱的方法,获取这个数字,前后不带空格。

您可以检查该行是否包含 192.168.0.0,如果句柄部分可以在该行的任何位置,则进行匹配。

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

例如,如果示例数据在file

awk '
/192\.168\.0\.0/ && match([=10=], /handle [0-9]+/) {
    print substr([=10=], RSTART+7)
    exit  
}' file

输出

223

如果 handle 部分总是在 ip 之后,您也可以使用 gnu awk 和捕获组:

awk '
match([=12=], /192\.168\.0\.0.*handle ([0-9]+)/, a) {
    print a[1]
    exit  
}' file

您显然缺少的信息是 awk 有一个名为 NF 的变量,用于表示字段数。并且您应该使用 -F grep 选项将 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
           }
       }')

awk 解决方案是您最简单的选择(以及您询问的内容),但是通过匹配 handle 和连续的数字。只是一个额外的选择,因为......为什么不呢。

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

最终的解决方案是“只有第一场比赛”,结果是这样的:

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

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