几年来,我经常需要将(已排序)文本的行与匹配的第一个字段组合在一起,而我从未找到过优雅(即单线程unix命令行)的方法。我想要的是与unix join
命令相似,但join
需要2个文件,每个键最多出现一次。我想从一个文件开始,其中一个键可能出现多个tile。
我有一个执行此操作的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 one-liner,如果你能想到一个简短而优雅的算法。这应该能够处理数百万行输入。有任何想法吗?
答案 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