几年来,我经常需要将(排序的)文本行与匹配的第一个字段相结合,但我从未找到一种优雅的(即单行 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
“比赛场”真的是可选的。它可能始终是第一个字段。分隔符的后续出现应被视为纯文本。
如果你能想到一个简短而优雅的算法,我不介意 perl、ruby、awk 单行。这应该能够处理数百万行输入。有任何想法吗?
最佳答案
使用 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
不同时,将始终满足因此是根据第一个字段隔离我们的记录的标准。a==$1
:如果匹配,则可能意味着您正在迭代属于前一个记录集的记录。在这种情况下,替换第一次出现的 $1:
(注意 :
)例如。 apple:
与 ;
. $1":"
也可以写成 $1FS
哪里FS is :
如果您的文件中有数百万行,那么这种方法将是最快的,因为它不涉及任何预处理,而且我们没有使用任何其他数据结构,比如数组来存储您的键或记录。
关于unix - 合并具有匹配第一个字段的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46734615/