2013-08-28 47 views
1

快速简洁地匹配来自具有匹配第一个字段的文本文件的行的方式。用于匹配具有匹配第一个字段的行的命令行(sed,awk等)

样品输入:

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 | merge line on the basis of field matching”,但是其他问题将过多的内容加载到内存中。我需要一个流媒体方法。

+6

解释为什么这是所需的输出,因为它根本不明显。例如你是否在寻找一个能让你指定b,d和e作为所需键值的工具,或者你在寻找键盘在输入中出现两次还是其他的情况? –

+0

我想合并行匹配的第一个字段。对不起,这不清楚。此外,输入是排序的。 –

回答

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 
+0

但是OP怎么能得到'Desired output ,替代? – anubhava

+0

您的方法很好,0m16.330s处理。时间awk -F \ | '$ 1 == prev_key {print prev_line;匹配++} $ 1!= prev_key {if(matches)print prev_line;匹配= 0; prev_key = $ 1; } {prev_line = $ 0} END {if(matches)print $ 0}'INFILE> OUTFILE –

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 
+0

谢谢。第二个领域是不可预知的长度,通常> 100个字符。顺便说一句,那些“uniq”的参数在MacOS和Ubuntu中都不可用。 –

+0

够公平的,这两个'awk'脚本应该为你做好诀窍。你确定它们不能在你的Ubuntu机器上使用吗?你有什么版本的coreutils。 'uniq --version - uniq(GNU coreutils)8.21' –

+0

谢谢!第二个是我真正需要的。你的方法运作良好;第一个为0m29.103s,第二个为0m34.036s。 –

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 
+4

当输入文件很大时会有很高的内存需求 –

+0

我对awk的担心是将所有内容加载到内存中,然后在END中调用它;但我的担忧可能是没有根据的。我会试试这个。谢谢! –

+1

令我惊讶的是,您的方法适用于我的0.5GB输入文件。处理时间0m19.184s。时间awk -F'|' '!(a中的$ 1){a [$ 1] = $ 2;下一步}在{b [$ 1] = b [$ 1] FS a [$ 1] FS $ 2} $ {1} END {for(i in b)print ib [i]}'INFILE> OUTFILE –

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 
+0

我试过了你的第二种方法。这很快,但我得到了一些虚假的点击。谢谢你的样品。 –

+0

错误点击?很难相信,如果您的实际输入看起来像您的示例输入,但如果您想分享您的输入以及您获得的不良输出,我很乐意看一看。 –

+0

埃德,我不是故意批评,而且错误可能在我身边。我只能说,当我进行快速测试时,输出结果并不符合我的预期。我的输入实际上比我给出的样本复杂得多,但仍然基本上是由管道分隔的两个字段的相同想法。我认为没有必要深入研究这一点。再次感谢。 –

1

这可能适合你(GNU sed):

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

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

+0

感谢您的支持。我已经使用过awk,但是它有一个sed解决方案。 –

+0

注意,在mac上,“sed -r”是“sed -E”;还请注意,您的方法对我来说并不适用,至少在我的Mac上,具有上述测试内容。 –