1

将文本文件中的行与匹配的第一个字段匹配的快速简洁的方法是什么。

样本输入:

a|lorem
b|ipsum
b|dolor
c|sit
d|amet
d|consectetur
e|adipisicing
e|elit

期望的输出:

b|ipsum
b|dolor
d|amet
d|consectetur
e|adipisicing
e|elit

所需的输出,替代:

b|ipsum|dolor
d|amet|consectetur
e|adipisicing|elit

我可以想出很多方法来写这个,但我怀疑有一个聪明的方法可以做到这一点,例如,使用 sed、awk 等。我的源文件大约为 0.5 GB。

这里有一些相关的问题,例如“ awk | 基于字段匹配的合并行”,但其他问题将太多内容加载到内存中。我需要一个流式传输方法。

4

5 回答 5

3

对于固定宽度的字段,您可以使用uniq

$ uniq -Dw 1 file
b|ipsum
b|dolor
d|amet
d|consectetur
e|adipisicing
e|elit

如果您没有固定宽度的字段,这里有两种awk解决方案:

awk -F'|' '{a[$1]++;b[$1]=(b[$1])?b[$1]RS$0:$0}END{for(k in a)if(a[k]>1)print b[k]}' file
b|ipsum
b|dolor
d|amet
d|consectetur
e|adipisicing
e|elit

awk -F'|' '{a[$1]++;b[$1]=b[$1]FS$2}END{for(k in a)if(a[k]>1)print k b[k]}' file
b|ipsum|dolor
d|amet|consectetur
e|adipisicing|elit
于 2013-08-28T16:31:33.810 回答
3

这是一种只需要记住上一行的方法(因此需要对输入文件进行排序)

awk -F \| '
    $1 == prev_key {print prev_line; matches ++}
    $1 != prev_key {                            
        if (matches) print prev_line
        matches = 0
        prev_key = $1
    }                
    {prev_line = $0}
    END { if (matches) print $0 }
' filename
b|ipsum
b|dolor
d|amet
d|consectetur
e|adipisicing
e|elit

备用输出

awk -F \| '
    $1 == prev_key {
        if (matches == 0) printf "%s", $1 
        printf "%s%s", FS, prev_value
        matches ++
    }             
    $1 != prev_key {
        if (matches) printf "%s%s\n", FS, prev_value
        matches = 0                                 
        prev_key = $1
    }                
    {prev_value = $2}
    END {if (matches) printf "%s%s\n", FS, $2}
' filename
b|ipsum|dolor
d|amet|consectetur
e|adipisicing|elit
于 2013-08-28T16:40:20.530 回答
1

使用 awk:

awk -F '|' '!($1 in a){a[$1]=$2; next} $1 in a{b[$1]=b[$1] FS a[$1] FS $2}
    END{for(i in b) print i b[i]}' file
d|amet|consectetur
e|adipisicing|elit
b|ipsum|dolor
于 2013-08-28T16:35:15.220 回答
1

这可能对您有用(GNU sed):

sed -r ':a;$!N;s/^(([^|]*\|).*)\n\2/\1|/;ta;/^([^\n|]*\|){2,}/P;D' /file

这会将 2 行读入模式空间,然后检查两行中的键是否相同。如果是这样,它会删除第二个键并重复。如果不是,它将检查第一行中是否存在两个以上的字段,如果存在则将其打印出来然后将其删除,否则它只会删除第一行。

于 2013-08-28T19:49:11.020 回答
0
$ awk -F'|' '$1 == prev {rec = rec RS $0; size++; next} {if (size>1) print rec; rec=$0; size=1} {prev = $1} END{if (size>1) print rec}' file
b|ipsum
b|dolor
d|amet
d|consectetur
e|adipisicing
e|elit

$ awk -F'|' '$1 == prev {rec = rec FS $2; size++; next} {if (size>1) print rec; rec=$0; size=1} {prev = $1} END{if (size>1) print rec}' file
b|ipsum|dolor
d|amet|consectetur
e|adipisicing|elit
于 2013-08-28T17:02:44.443 回答