使用 sed/awk/perl 进行文本处理

使用 sed/awk/perl 进行文本处理

我有很多行类似这种模式的文本。我可以仅将值和名称分组到一行中吗?

ABCDEFG_10_node10:2154  ABCDEFG_10_node10:54
ABCDEFG_10_node10:2254  ABCDEFG_10_node10:64
ABCDEFG_10_node10:410 ABCDEFG_10_node10:10
ABCDEFG_10_node10:210 ABCDEFG_10_node10:10 
ABCDEFG_10_node10:365
ABCDEFG_10_node10:890
ABCDEFG_10_node10:741
XXYZZ_71_node2:24: XXYZZ_71_node2:504:
X3y5z_53_node1:664: X3y5z_53_node1:990:
RCTY_11_node2:224: RCTY_11_node2:234:

预期输出:

ABCDEFG_10_node10: 2154,2254,410,210,365,890,741,54,64,10,10
XXYZZ_71_node2: 24,504
X3y5z_53_node1: 664,990
RCTY_11_node2: 224,234

我在AIX上。我怎样才能做到这一点?

答案1

Perl 方法(假设顺序不重要):

$ perl -lne 'while(/(\w+):(\d+)/g){
                push @{$k{$1}},$2
             } 
             END{
                print "$_: " . join ",", @{$k{$_}} for keys %k
             }' file 
ABCDEFG_10_node10: 2154,54,2254,64,410,10,210,10,365,890,741

它逐行读取输入文件 ( -ln) 并运行由 给出的脚本-e。将while(/(\w+):(\d+)/g)收集所有非空白实例,然后是 a :,然后是更多非空白。由于它们被捕获在括号中,因此$1将是名称和$2值。然后将它们推入数组的哈希值(hash %k,其值是数组)。最后,我们打印哈希的每个键(名称)及其值的数组,并通过,.

如果你重视简洁性,你可以将上面的内容写成一行:

perl -lne 'while(/(\S+):(\S+)/g){push @{$k{$1}},$2}}{$"=",";print"$_: @{$k{$_}}" for keys%k' file

超越了易读性:

perl -nE'push@{$k{$1}},$2while/(\w+):(\d+)/g}{$"=",";say"$_: @{$k{$_}}"for keys%k' file

答案2

awk解决方案:

awk -F':|[[:space:]]+' '{ 
         a[$1]=a[$1]? a[$1]","$2:$2; if(NF==4) b[$3]=b[$3]? b[$3]","$4:$4 
     }
     END{ for(i in a) printf "%s: %s%s\n",i,a[i],(i in b)? ","b[i]:"" }' file

  • -F':|[[:space:]]+'- 复杂的字段分隔符

  • a[$1]=a[$1]? a[$1]","$2:$2- 对每个唯一名称的值进行分组ABCDEFG...

  • if(NF==4) b[$3]=b[$3]? b[$3]","$4:$4- 如果有附加的右侧部分 - 将值分组到附加数组中b


输出:

ABCDEFG_10_node10: 2154,2254,410,210,365,890,741,54,64,10

----------

如果值的顺序不重要,则可以稍微简化上述方法:

awk -F':|[[:space:]]+' '{ 
        a[$1]=a[$1]? a[$1]","$2:$2; if(NF==4) a[$3]=a[$3]? a[$3]","$4:$4 
     }
     END{ for(i in a) print i":",a[i] }' file

答案3

awk '{ for (i=1;i<=NF;i++) { split($NF,arr,":");if (dat[arr[1]]=="") { dat[arr[1]]=arr[2] } else { dat[arr[1]]=dat[arr[1]]","arr[2] } } } END { for ( i in dat ) { print i": "dat[i] } }' filename

Roman 的另一种 awk 解决方案,我们依次获取每个空格分隔的数据,然后根据字符使用数组 arr 中的 split 函数进一步拆分数据:然后我们使用字符串构建一个以 ABC 等字符串为键的数组要打印的数字。然后,我们循环遍历这个数组 (dat),构建一个以键、: 和字符串开头的字符串。然后将其打印出来。

答案4

使用 Raku(以前称为 Perl_6)

raku -e 'say .key, " => ", .value.words[1,3,5...*] for lines.split(/<[:\s]>/, :skip-empty).rotor(2).classify( *.[0]);'

输入示例:

ABCDEFG_10_node10:2154  ABCDEFG_10_node10:54
ABCDEFG_10_node10:2254  ABCDEFG_10_node10:64
ABCDEFG_10_node10:410 ABCDEFG_10_node10:10
ABCDEFG_10_node10:210 ABCDEFG_10_node10:10 
ABCDEFG_10_node10:365
ABCDEFG_10_node10:890
ABCDEFG_10_node10:741
XXYZZ_71_node2:24: XXYZZ_71_node2:504:
X3y5z_53_node1:664: X3y5z_53_node1:990:
RCTY_11_node2:224: RCTY_11_node2:234:

示例输出:

XXYZZ_71_node2 => (24 504)
RCTY_11_node2 => (224 234)
ABCDEFG_10_node10 => (2154 54 2254 64 410 10 210 10 365 890 741)
X3y5z_53_node1 => (664 990)

简而言之,lines读入,破坏性地split读取:and \s(通过 省略任何空元素:skip-empty),rotor(连接)每 2 个元素,并classify通过每对中的第一个元素进行 -ing。 [我可以打电话.classify( *.[0].unique)澄清意图,但 Raku 无论如何都做了正确的事情]。

如果OP确实想要他/她的帖子中指定的格式,则替换=>为并在调用末尾:添加一个调用,因此之前的整个部分如下:join.valueforsay .key, ": ", .value.words[1,3,5...*].join(",")

输出示例 (2):

X3y5z_53_node1: 664,990
RCTY_11_node2: 224,234
ABCDEFG_10_node10: 2154,54,2254,64,410,10,210,10,365,890,741
XXYZZ_71_node2: 24,504

https://docs.raku.org/routine/classify
https://raku.org

相关内容