Mic*_*elD 5 unix awk command-line bsd join
几年来,我经常需要将(已排序)文本的行与匹配的第一个字段组合在一起,而我从未找到过优雅(即单行unix命令行)的方法.我想要的是与unix join
命令的相似之处,但是join
需要2个文件,每个键最多出现一次.我想从一个文件开始,其中一个键可能出现多个tile.
我有一个执行此操作的ruby和perl脚本,但是没有办法将我的算法缩短为单行.经过多年的UNIX的使用,我还在学习新的技巧有comm
,paste
,uniq
,等,我怀疑有一个聪明的办法来做到这一点.
有一些相关的问题,比如将所有具有相同第一列的行连接到同一行 ; 命令行匹配第一个字段匹配的行(sed,awk等) ; 并将线条与匹配的键组合在一起 - 但这些解决方案从未真正提供干净可靠的解决方案.
这是示例输入:
apple:A fruit
apple:Type of: pie
banana:tropical fruit
cherry:small burgundy fruit
cherry:1 for me to eat
cherry:bright red
Run Code Online (Sandbox Code Playgroud)
这是示例输出:
apple:A fruit;Type of: pie
banana:tropical fruit
cherry:small burgundy fruit;1 for me to eat;bright red
Run Code Online (Sandbox Code Playgroud)
这是我理想的语法:
merge --inputDelimiter=":" --outputDelimiter=";" --matchfield=1 infile.txt
Run Code Online (Sandbox Code Playgroud)
"matchfield"实际上是可选的.它可能永远是第一个领域.分隔符的后续出现应该被视为纯文本.
我不介意perl,ruby,awk单行,如果你能想到一个简短而优雅的算法.这应该能够处理数百万行输入.有任何想法吗?
使用awk一个衬垫
awk -F: -v ORS="" 'a!=$1{a=$1; $0=RS $0} a==$1{ sub($1":",";") } 1' file
Run Code Online (Sandbox Code Playgroud)
输出:
apple:A fruit;Type of: pie
banana:tropical fruit
cherry:small burgundy fruit;1 for me to eat;bright red
Run Code Online (Sandbox Code Playgroud)
环境ORS=""
; 默认情况下是 \n
.
我们设置 (Output Record Separator) 的原因ORS=""
是因为我们不希望 awk 在每条记录末尾的输出中包含换行符。我们想以我们自己的方式、通过我们自己的逻辑来处理它。实际上,我们在第一个字段与前一个字段不同的每条记录的开头都包含换行符。
a!=$1
:当变量a
(最初为空)与第一个字段不匹配$1
时,例如。apple
在第一行,然后设置a=$1
ie$0=RS $0
或$0
简单地whole record
变成"\n"$0
(基本上在记录的开头添加换行符)。 a!=$1
当第一个字段 ( $1
) 与前一行不同时,将始终满足$1
,因此是根据第一个字段分隔记录的标准。
a==$1
:如果匹配,则可能意味着您正在迭代属于前一个记录集的记录。在这种情况下,用第一次出现的$1:
(注意:
)代替例如。apple:
和;
。 也$1":"
可以写成$1FS
FS is :
如果您的文件中有数百万行,那么这种方法将是最快的,因为它不涉及任何预处理,而且我们不使用任何其他数据结构(例如数组)来存储您的键或记录。
发现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
Run Code Online (Sandbox Code Playgroud)
输出:
apple:A fruit;Type of: pie
banana:tropical fruit
cherry:small burgundy fruit;1 for me to eat;bright red
Run Code Online (Sandbox Code Playgroud)