sed/awk:替换最后一次出现“.”之后的行中的数字

sed/awk:替换最后一次出现“.”之后的行中的数字

我有以下 tcpdump 流:

Current:
07:36:03.848461 IP 172.17.3.41.33101 > 172.17.3.43.17408: UDP, length 44
07:36:03.848463 IP 172.17.3.42.33101 > 172.17.3.43.17409: UDP, length 44
07:36:03.848467 IP SYSTEM-A.33101 > 172.17.3.43.17418: UDP, length 45
07:36:03.848467 IP SYSTEM-B.33101 > 172.17.3.43.17419: UDP, length 45

端口号以十进制表示。如何将其通过管道传输到 sed 或 awk 来修改流,使其与端口号更改为十六进制的相同流:

Expected:
07:36:03.848461 IP 172.17.3.41.814d > 172.17.3.43.4400: UDP, length 44
07:36:03.848463 IP 172.17.3.42.814d > 172.17.3.43.4401: UDP, length 44
07:36:03.848467 IP SYSTEM-A.814d > 172.17.3.43.440a: UDP, length 45
07:36:03.848467 IP SYSTEM-B.814d > 172.17.3.43.440b: UDP, length 45

如果我有端口号,我用它将其转换为十六进制:

echo 33101 | sed  -e 's/.*://' | xargs printf "%x\n"
814d

我一直在尝试解决这个问题,但没有运气。如何替换'.'流的第三列和第五列中最后一次出现后的端口号,然后即时将其更改为十六进制?

答案1

perl

perl -pe 's/\s\S+\.\K\d+/sprintf "%x", $&/ge' < your-file

它查找由空格 ( \s) 和后跟组成的单词一个或多个的序列( +)非空白( \S)、一个点和一个或多个数字的序列 ( ) 并用十进制格式的相同 ( )\d+替换结束部分(其开头标记为) (总体上,替换被评估为 Perl 代码)。\K$&xge

答案2

在每个 UNIX 机器上的任何 shell 中使用任何 awk:

$ cat tst.awk
function mkPortHex(fldNr,       port, sfx) {
    port = sfx = $fldNr
    sub(/.*\./,"",port)
    sub(/.*[0-9]/,"",sfx)
    sub(/[^.]+$/,sprintf("%x%s",port,sfx),$fldNr)
}
{
    mkPortHex(3)
    mkPortHex(5)
    print
}

$ awk -f tst.awk file
07:36:03.848461 IP 172.17.3.41.814d > 172.17.3.43.4400: UDP, length 44
07:36:03.848463 IP 172.17.3.42.814d > 172.17.3.43.4401: UDP, length 44
07:36:03.848467 IP SYSTEM-A.814d > 172.17.3.43.440a: UDP, length 45
07:36:03.848467 IP SYSTEM-B.814d > 172.17.3.43.440b: UDP, length 45

使用 GNU awk 将第三个参数匹配到 match():

$ cat tst.awk
function mkPortHex(fldNr) {
    match($fldNr,/(.*\.)([0-9]+)(:?)/,a)
    $fldNr = a[1] sprintf("%x",a[2]) a[3]
}
{
    mkPortHex(3)
    mkPortHex(5)
    print
}

$ awk -f tst.awk file
07:36:03.848461 IP 172.17.3.41.814d > 172.17.3.43.4400: UDP, length 44
07:36:03.848463 IP 172.17.3.42.814d > 172.17.3.43.4401: UDP, length 44
07:36:03.848467 IP SYSTEM-A.814d > 172.17.3.43.440a: UDP, length 45
07:36:03.848467 IP SYSTEM-B.814d > 172.17.3.43.440b: UDP, length 45

答案3

如果字段编号是恒定的 - 正如您的问题字段 3 和 5 中所示 - 尝试

awk '
function CHX(FLD)   {n = split ($FLD, T, ".")
                     sub (T[n] "$", sprintf ("%X", T[n]), $FLD)
                    }
    {CHX(3)
     CHX(5)
    }
1
' file
07:36:03.848461 IP 172.17.3.41.814D > 172.17.3.43.4400 UDP, length 44
07:36:03.848463 IP 172.17.3.42.814D > 172.17.3.43.4401 UDP, length 44
07:36:03.848467 IP SYSTEM-A.814D > 172.17.3.43.440A UDP, length 45
07:36:03.848467 IP SYSTEM-B.814D > 172.17.3.43.440B UDP, length 45

例如,字段 5 中的尾随冒号:

awk '
function CHX(FLD)       {n = split ($FLD, T, "[^0-9]")
                         TRM = ""
                         if (!T[n])     {n--
                                         TRM = substr ($FLD, length($FLD))
                                        }
                         sub (T[n] TRM "$", sprintf ("%X%s", T[n], TRM), $FLD)
                        }
        {CHX(3)
         CHX(5)
        }
1
' file

答案4

谢谢大家的回答!他们都工作!不过,我也想在这里发布我的解决方案。现在我知道我特别要求这样sed做,awk但我的输入是一个tcpdump流,我想将端口号修改为十六进制。所以我浏览了源代码并更改了以下几行:

(void)snprintf(buf, sizeof(buf), "%u", i);

(void)snprintf(buf, sizeof(buf), "%x", i); // prints hexadecimal

(void)snprintf(buf, sizeof(buf), "%u", i);

(void)snprintf(buf, sizeof(buf), "%x", i); // prints hexadecimal

编译了二进制文件,现在 tcpdump 以十六进制打印端口。

相关内容