【问题标题】:awk - extract unique occurrences field 1, append multiple instances of field 2 on the same line in outputawk - 提取唯一出现的字段 1,在输出的同一行附加字段 2 的多个实例
【发布时间】:2017-05-20 05:55:29
【问题描述】:

我正在从一个大型路由器配置文件中提取服务对象(端口/协议)。使用 awk,我希望能够获取 $5 的唯一实例并将它们打印在一行上,并在 $5 的唯一实例之后打印 $7 中的各种值,用逗号分隔。

输入数据:

set resources group port-group ServiceA port '1'
set resources group port-group ServiceA port '2'
set resources group port-group ServiceA port '3'
set resources group port-group ServiceB port '10'
set resources group port-group ServiceA port '1'
set resources group port-group ServiceA port '2'
set resources group port-group ServiceA port '3'
set resources group port-group ServiceB port '10'
set resources group port-group ServiceB port '20'
set resources group port-group ServiceC port '30'
set resources group port-group ServiceC port '40'
set resources group port-group ServiceD port '50'
set resources group port-group ServiceD port '5050'
set resources group port-group ServiceD port '60'
set resources group port-group ServiceD port '65'
set resources group port-group ServiceD port '66'
set resources group port-group ServiceD port '89'

期望的输出:

set resources group port-group ServiceA port 1, 2, 3
set resources group port-group ServiceB port 10, 20
set resources group port-group ServiceC port 30, 40
set resources group port-group ServiceD port 50, 5050, 60, 65, 66, 89

到目前为止,我尝试 awk 语句的尝试都没有结果。

我尝试过的(它是脚本的一部分,所以有 CR。)

awk '{
gsub(/[:\47]/,"")}; i=!seen[$5]++; {print i,$7 } ' inputfile.txt

这给了我以下输出:

set resources group port-group ServiceA port 1
1 1
0 2
0 3
set resources group port-group ServiceB port 8
1 8
0 1
0 2
0 3
0 8
0 3
set resources group port-group ServiceC port 2
1 2
0 3
set resources group port-group ServiceD port 8
1 8
0 5050
0 3
0 83
0 1
0 2
0 990
0 3000
0 3001
0 3002
0 3003

我假设我必须使用带有 for 循环的多维数组来完成此操作,但我被卡住了。任何帮助表示赞赏!

【问题讨论】:

  • 你自己尝试了什么?
  • 到目前为止,我已经尝试了 awk ' !seen[$5]++' ,但没有获得太大的吸引力。也一直在使用 gsub 从输出中删除 ' 符号,这似乎是有效的。
  • 更新您的 exact 代码作为问题的一部分
  • 用代码更新帖子。

标签: linux awk


【解决方案1】:

awk解决方案:

awk '!a[$5]{a[$5]=$0; uniq[$5,$7]=$7}{ if ($5 in a && uniq[$5,$7]!=$7){ 
      a[$5]=a[$5]","$7; uniq[$5,$7]=$7}}END{for(i in a) print a[i]}' inputfile.txt

输出:

set resources group port-group ServiceA port '1','2','3'
set resources group port-group ServiceB port '10','20'
set resources group port-group ServiceC port '30','40'
set resources group port-group ServiceD port '50','5050','60','65','66','89'

  • !a[$5]{a[$5]=$0; uniq[$5,$7]=$7} - 在唯一的第 5 个字段值第一次出现时捕获行

  • if($5 in a && uniq[$5,$7]!=$7) - 检查相同 Service... 的重复值

  • uniq 数组,如果用于累积第 5 和第 7 字段的唯一绑定

  • a[$5]=a[$5]","$7 - 在关键行的末尾添加下一个唯一值


要获取不带单引号的值,请使用以下方法:

group_port_values.awk 脚本:

#!/bin/awk -f
BEGIN { FS="[ ']" }
!a[$5] {
    a[$5] = $0; 
    uniq[$5,$8] = $8
}
{
    if ($5 in a && uniq[$5,$8] != $8) { 
        a[$5] = a[$5]", "$8; 
        uniq[$5,$8] = $8
    }
}
END {
    for (i in a) {
        gsub(/\047/,"",a[i]);
        print a[i]
    }
}

用法

awk -f group_port_values.awk inputfile.txt

输出:

set resources group port-group ServiceA port 1, 2, 3
set resources group port-group ServiceB port 10, 20
set resources group port-group ServiceC port 30, 40
set resources group port-group ServiceD port 50, 5050, 60, 65, 66, 89

【讨论】:

  • @TomA.,不客气。如果你想要没有单引号的值 - 使用我的第二种方法
  • @EdMorton,至于分隔符,理想情况下,是的,错过了。那是固定的。您也可以查看我的第二种方法
  • 老实说,它有点难以阅读(通过缩进将其展开几行会有所帮助)所以我要建议的是,如果您在脚本开头添加 {gsub(/\047/,"")}那么您不需要在循环中多次执行此操作。另请注意,我使用/ 而不是" 作为第一个参数分隔符,因为它是正则表达式而不是字符串。实际上 - 由于您在 FS 中包含 ',您是否需要 gsub() ?我希望看到其中之一,但不会同时看到两者。
  • @EdMorton,让我的第二种方法更具可读性。至于在脚本开头移动gsub(/\047/,"") - 我对此表示怀疑,因为在这种情况下,它会尝试替换每个输入行。虽然我的方法将只替换 4 行而不是 17 行。你觉得如何?
  • 我认为您的循环每个端口执行一次,您的代码现在非常更易于阅读!
【解决方案2】:

TGIF!这是使用 2D 数组和不良编码习惯的 GNU awk 的一个(:

$ awk '
++a[$5 , $7]==1 {                   # if not seen before
    b[$5][++c[$5]]=$7 }             # hash it to b[key][index]
END{ 
    for(i in b) {                   # for all keys
        for(j=1;j<=c[i];j++)        # and all its indexes
            d=(j==1?"":d",")b[i][j] # gather buffer
        sub($5,i)                   # use the last known $0
        sub($NF,d)                  # and replace key and buffer to it
        print }                     # output
}' file
set resources group port-group ServiceA port '1','2','3'
set resources group port-group ServiceB port '10','20'
set resources group port-group ServiceC port '30','40'
set resources group port-group ServiceD port '50','5050','60','65','66','89'

【讨论】:

  • 也很有用。很高兴看到完成同一任务的多种方式!
  • @EdMorton , > FS > OFS 检查。谢谢。
【解决方案3】:
$ cat tst.awk
BEGIN { OFS=", " }
{ gsub(/\047/,""); pfx=$1 FS $2 FS $3 FS $4 }
$5 != prev { prt(prev); prev=$5 }
!seen[$7]++ { ports[++numPorts] = $7 }
END { prt(prev) }

function prt(sg) {
    if ( sg != "" ) {
        printf "%s %s ", pfx, sg
        for (portNr=1; portNr<=numPorts; portNr++) {
            printf "%s%s", ports[portNr], (portNr<numPorts ? OFS : ORS)
        }
        delete ports
        delete seen
        numPorts = 0
    }
}

$ sort file | awk -f tst.awk
set resources group port-group ServiceA 1, 2, 3
set resources group port-group ServiceB 10, 20
set resources group port-group ServiceC 30, 40
set resources group port-group ServiceD 50, 5050, 60, 65, 66, 89

【讨论】:

  • 看起来您的输出具有重复的行/值,并且与所需的输出不同。有什么原因吗?
  • @RomanPerekhrest 是的,我没有注意到输入中所有 ServiceAs 中间的 ServiceB。感谢您发现它,现在更正。
猜你喜欢
  • 1970-01-01
  • 2019-08-09
  • 1970-01-01
  • 2011-02-19
  • 1970-01-01
  • 1970-01-01
  • 2014-03-17
  • 2011-07-04
  • 2016-12-23
相关资源
最近更新 更多