将线条与匹配的第一个字段组合

时间:2017-10-13 16:51:54

标签: unix join awk command-line bsd

几年来,我经常需要将(已排序)文本的行与匹配的第一个字段组合在一起,而我从未找到过优雅(即单线程unix命令行)的方法。我想要的是与unix join命令相似,但join需要2个文件,每个键最多出现一次。我想从一个文件开始,其中一个键可能出现多个tile。

我有一个执行此操作的ruby和perl脚本,但是没有办法将我的算法缩短为单行。经过多年的unix使用,我仍在学习commpasteuniq等新技巧,我怀疑这是一种聪明的方法。

有一些相关问题,例如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 one-liner,如果你能想到一个简短而优雅的算法。这应该能够处理数百万行输入。有任何想法吗?

4 个答案:

答案 0 :(得分:3)

使用 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(最初为空)与第一个字段$1不匹配时,例如。在第一行apple,然后设置a=$1$0=RS $0$0或简称whole record变为"\n"$0(基本上在记录开头添加换行符)。如果第一个字段(a!=$1)与前一行$1不同,则$1将始终满足,因此是根据第一个字段隔离记录的标准。< / p>

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

如果您的文件中有数百万行,那么这种方法将是最快的,因为它不涉及任何预处理,而且我们也没有使用任何其他数据结构来说存储您的密钥或记录。

答案 1 :(得分:2)

发现 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

答案 2 :(得分:1)

<input type="hidden" id="answer" value="">
<script>
    document.getElementById("answer").value=correct;
</script>
<input type="button" onclick="checkAnswer('answer')" value="Submit"/>

不确定它是否符合“优雅”的标准,但它确实有效,但我确定数百万行不会很快 - 随着for F in `cut -f1 -d ':' infile.txt | sort | uniq`; do echo "$F:$(grep $F infile.txt | cut -f2- -d ':' | paste -s -d ';' - )"; done 次呼叫数量的增加,它会显着减慢。您期望哪些匹配字段是唯一的?

答案 3 :(得分:1)

我认为这个人做的工作

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