结合具有匹配第一个字段的行

结合具有匹配第一个字段的行

问题描述:

几年来,我经常需要将(排序的)文本行与匹配的第一个字段组合起来,并且我从来没有找到一种优雅的(即单行unix命令行)做到这一点。我想要的与unix join命令的可能性类似,但join需要2个文件,每个键最多显示一次。我想从一个文件开始,其中一个密钥可能会出现多个图块。结合具有匹配第一个字段的行

我有一个ruby和perl脚本来做到这一点,但没有办法将我的算法缩短为一行。经过多年的unix使用,我仍在学习comm,paste,uniq等新的技巧,我怀疑有一个聪明的方法来做到这一点。

还有一些相关的问题,如join all lines that have the same first column to the same line; Command line to match lines with matching first field (sed, awk, etc.);和Combine lines with matching keys - 但这些解决方案从来没有真正提供一个干净和可靠的解决方案。

这里的样本输入:

apple:A fruit 
apple:Type of: pie 
banana:tropical fruit 
cherry:small burgundy fruit 
cherry:1 for me to eat 
cherry:bright red 

这里的示例输出:

apple:A fruit;Type of: pie 
banana:tropical fruit 
cherry:small burgundy fruit;1 for me to eat;bright red 

这里是我理想中的语法:

merge --inputDelimiter=":" --outputDelimiter=";" --matchfield=1 infile.txt 

的 “matchfield” 实在是可选的。它可能永远是第一个领域。分隔符的后续显示应该像纯文本一样对待。

我不介意perl,ruby,awk单行程,如果你能想到一个简短而优雅的算法。这应该能够处理数百万行的输入。有任何想法吗?

发现AWK语言:

awk -F':' '{ v=substr($0, index($0,":")+1); a[$1]=($1 in a? a[$1]";" : "")v } 
      END{ for(i in a) print i,a[i] }' OFS=':' infile.txt 

输出:

apple:A fruit;Type of: pie 
banana:tropical fruit 
cherry:small burgundy fruit;1 for me to eat;bright red 
+0

感谢@RomanPerekhrest,工作。比过去我尝试过的其他awk解决方案要好得多,这些解决方案会在复杂的线路上突破。也就是说,我仍然喜欢用简单的语法来缩短命令,但我很乐意拥有一行代码。 – MichaelD

for F in `cut -f1 -d ':' infile.txt | sort | uniq`; do echo "$F:$(grep $F infile.txt | cut -f2- -d ':' | paste -s -d ';' -)"; done 

不知道它有资格作为 '高雅',但它的作品,但我敢肯定不会很快对于数百万行 - 随着grep调用次数的增加,它会显着减慢。你期望什么比例匹配字段是唯一的?

+0

感谢您的unix字符串。我预计重复密钥/匹配字段的重复次数大约是1-5次,因此在一百万行中可能会有30万个密钥。 – MichaelD

+0

啊,30万次grep调用是不合理的。感谢您的反馈 – jgrundstad

我觉得这个做的工作

awk -F':' '$1!=a{if(b);print b;b=""}a=$1{$1="";if(!b)b=a;b=b$0}END{print b}' infile 
+2

你能解释一下吗? – ghoti

使用AWK一个衬垫

awk -F: -v ORS="" 'a!=$1{a=$1; $0=RS $0} a==$1{ sub($1":",";") } 1' file 

输出:

apple:A fruit;Type of: pie 
banana:tropical fruit 
cherry:small burgundy fruit;1 for me to eat;bright red 

设置ORS="";默认情况下它是\n
我们设置ORS=""(输出记录分隔符)的原因是因为我们不希望awk在每条记录末尾的输出中包含换行符。我们希望通过我们自己的逻辑以我们自己的方式处理它。实际上,我们实际上在每条记录的开头都包含换行符,这些记录的第一个字段与前一个字段不同。

a!=$1:当变量a(最初为null)与第一个字段$1不匹配时,例如, apple在第一行,然后设置a=$1$0=RS $0,即$0或者简单地whole record变成"\n"$0(基本上在记录开始处添加换行符)。当第一个字段($1)比前一行的$1有不同时,a!=$1将始终满足,因此是根据第一个字段分隔我们记录的标准。

a==$1:如果它匹配,那么它可能意味着您正在迭代属于上一个记录集的记录。在这种情况下,替代第一次出现$1:(注意:)例如。 apple:;$1":"也可以写为$1FS,其中FS is :

如果你有几百万行的在你的文件,则该方法将是最快的,因为它不涉及任何预处理,也是我们没有使用任何其他数据结构数组说用于存储您的密钥或记录。

+0

感谢您的好解释。 – MichaelD

+0

@迈克尔D:欢迎迈克尔。 – batMan