Bash 删除大括号中的重复行

Bash 删除大括号中的重复行,bash,perl,awk,sed,Bash,Perl,Awk,Sed,我有一个包含以下内容的文件: l1_lololo { abcdef vgjhklfgkchbnvu gfuhjfythkjbgftyhkjgyftuihgt6 deefgik abcdef } l2_blabla { 123456 vgghyfthjfgtrdygfhhbnvu gfuhjgvftdyfgvjgyftuihgt6 deiulouk 123456 } 我需要用sed/awk/bash/等检查大括号中

我有一个包含以下内容的文件:

l1_lololo {
    abcdef
    vgjhklfgkchbnvu
    gfuhjfythkjbgftyhkjgyftuihgt6
    deefgik
    abcdef
}

l2_blabla {
    123456
    vgghyfthjfgtrdygfhhbnvu
    gfuhjgvftdyfgvjgyftuihgt6
    deiulouk
    123456
}
我需要用sed/awk/bash/等检查大括号中的文本,并删除重复的行,在每个大括号中只保留第一个循环行,我需要得到以下信息:

l1_lololo {
    abcdef
    vgjhklfgkchbnvu
    gfuhjfythkjbgftyhkjgyftuihgt6
    deefgik
}

l2_blabla {
    123456
    vgghyfthjfgtrdygfhhbnvu
    gfuhjgvftdyfgvjgyftuihgt6
    deiulouk
}

如何做到这一点?

如果您可以保证块以仅包含}的行结束,则可以简单地执行以下操作:

awk '/^}$/ {delete a} !a[$0]++' input

如果您需要一个更健壮的解决方案,也许只需在模式中添加一些空白来匹配块的结尾。但是,如果您想要一个完整的解析器,并且希望仔细地匹配大括号,那么awk可能不适合此任务。

如果您可以保证块以只包含}的行结束,那么可以简单地执行以下操作:

awk '/^}$/ {delete a} !a[$0]++' input

如果您需要一个更健壮的解决方案,也许只需在模式中添加一些空白来匹配块的结尾。但是,如果您想要一个完整的解析器,并且希望仔细地匹配大括号,那么awk可能不适合此任务。

使用存储在散列中的以下代码数据可以获得所需的结果

use strict;
use warnings;
use feature 'say';

my $data = do{ local $/; <DATA> };      # read whole data

my %seen;
my %records = $data =~ /(\w+)\s+\{\s*(.*?)\s*\}/sg;     # split into records

while( my($k,$v) = each %records ) {    # for each record split into array
    my @array = map { if( not $seen{$_} ) { $seen{$_} = 1; $_ } } split '\s+', $records{$k};    # store uniq elements
    pop @array;                         # pop out last empty element
    $records{$k} = \@array;             # store array in hash
}

while( my($k,$v) = each %records ) {    # each record
    say "$k = {";                       # output hash key
    say "\t$_" for @{$v};               # output each element of array
    say "}\n";                          # done
}

__DATA__
l1_lololo {
    abcdef
    vgjhklfgkchbnvu
    gfuhjfythkjbgftyhkjgyftuihgt6
    deefgik
    abcdef
}

l2_blabla {
    123456
    vgghyfthjfgtrdygfhhbnvu
    gfuhjgvftdyfgvjgyftuihgt6
    deiulouk
    123456
}

使用存储在散列中的以下代码数据可以获得期望的结果

use strict;
use warnings;
use feature 'say';

my $data = do{ local $/; <DATA> };      # read whole data

my %seen;
my %records = $data =~ /(\w+)\s+\{\s*(.*?)\s*\}/sg;     # split into records

while( my($k,$v) = each %records ) {    # for each record split into array
    my @array = map { if( not $seen{$_} ) { $seen{$_} = 1; $_ } } split '\s+', $records{$k};    # store uniq elements
    pop @array;                         # pop out last empty element
    $records{$k} = \@array;             # store array in hash
}

while( my($k,$v) = each %records ) {    # each record
    say "$k = {";                       # output hash key
    say "\t$_" for @{$v};               # output each element of array
    say "}\n";                          # done
}

__DATA__
l1_lololo {
    abcdef
    vgjhklfgkchbnvu
    gfuhjfythkjbgftyhkjgyftuihgt6
    deefgik
    abcdef
}

l2_blabla {
    123456
    vgghyfthjfgtrdygfhhbnvu
    gfuhjgvftdyfgvjgyftuihgt6
    deiulouk
    123456
}

如果您对其他语言开放,这在tcl中非常容易做到,因为输入是tcl列表格式,允许您使用它进行所有解析,而不需要任何可能脆弱的正则表达式:

#!/usr/bin/env tclsh
package require Tcl 8.5
foreach {key lst} [read stdin] {
    foreach item $lst { dict set seen $item 1 }
    puts "$key {\n\t[join [dict keys $seen] \n\t]\n}\n"
    unset seen
}
例如:

$ ./dedup < input.txt
l1_lololo {
        abcdef
        vgjhklfgkchbnvu
        gfuhjfythkjbgftyhkjgyftuihgt6
        deefgik
}

l2_blabla {
        123456
        vgghyfthjfgtrdygfhhbnvu
        gfuhjgvftdyfgvjgyftuihgt6
        deiulouk
}

如果您对其他语言开放,这在tcl中非常容易做到,因为输入是tcl列表格式,允许您使用它进行所有解析,而不需要任何可能脆弱的正则表达式:

#!/usr/bin/env tclsh
package require Tcl 8.5
foreach {key lst} [read stdin] {
    foreach item $lst { dict set seen $item 1 }
    puts "$key {\n\t[join [dict keys $seen] \n\t]\n}\n"
    unset seen
}
例如:

$ ./dedup < input.txt
l1_lololo {
        abcdef
        vgjhklfgkchbnvu
        gfuhjfythkjbgftyhkjgyftuihgt6
        deefgik
}

l2_blabla {
        123456
        vgghyfthjfgtrdygfhhbnvu
        gfuhjgvftdyfgvjgyftuihgt6
        deiulouk
}

这可能适用于GNU sed:

sed -E '/^\S+ \{/{:a;N;s/((\n[^\n]*)(\n.*)*)\2$/\1/;/\n\}$/!ba}' file

如果一行以某个文本开头,后跟一个{,则追加下一行,如果最后一行与前一行匹配,则删除最后一行。重复后一行,直到一行只包含一个},然后打印结果。

这可能适用于您所使用的GNU:

sed -E '/^\S+ \{/{:a;N;s/((\n[^\n]*)(\n.*)*)\2$/\1/;/\n\}$/!ba}' file

如果一行以某个文本开头,后跟{,则追加下一行,如果最后一行与前一行匹配,则删除最后一行。重复后一行,直到一行只包含},然后打印结果。

但如果块在大括号前以空格结尾,并且可能包含,例如,;'在大括号之后,我需要在命令中更改什么?您只需要更改模式以匹配它。例如/^*}/可能适合您。匹配任何以空格开头,后跟}@ComptrollerChanel的行,如果在实际数据中,块在大括号之前以空格结尾,并且可能包含,例如,;'在大括号之后,显然,您应该在问题的示例输入/输出中包含这些情况。当我们试图回答您的问题时,我们所要做的就是您在问题中提供的信息,因此您的示例必须真正代表您的真实数据如示例所示,如果数据中的其他任何地方都不能有},则只需要一个}。但如果块在大括号之前以空格结尾,并且可能包含,例如,;'在大括号之后,我需要在命令中更改什么?您只需要更改模式以匹配它。例如/^*}/可能适合您。匹配任何以空格开头,后跟}@ComptrollerChanel的行,如果在实际数据中,块在大括号之前以空格结尾,并且可能包含,例如,;'在大括号之后,显然,您应该在问题的示例输入/输出中包含这些情况。当我们试图回答您的问题时,我们所要做的就是您在问题中提供的信息,因此您的示例必须真正代表您的真实数据如示例所示,如果数据中的其他任何地方都不能有},则只需使用}。