用于合并具有匹配的第一个字段,50 GB输入的行的命令行

时间:2015-07-30 16:24:22

标签: regex optimization awk sed

前段时间,我问了一个关于合并具有共同第一个字段的行的问题。这是原作:Command line to match lines with matching first field (sed, awk, etc.)

示例输入:

a|lorem
b|ipsum
b|dolor
c|sit
d|amet
d|consectetur
e|adipisicing
e|elit

期望的输出:

b|ipsum|dolor
d|amet|consectetur
e|adipisicing|elit

这个想法是,如果第一个字段匹配,则合并这些行。输入已排序。实际内容更复杂,但使用管道作为唯一分隔符。

前一个问题中提供的方法在我的0.5GB文件上运行良好,处理时间约为16秒。但是,我的新文件大约大100倍,我更喜欢流式传输的方法。从理论上讲,这将能够在约30分钟内运行。运行24小时后,先前的方法无法完成。

在MacOS上运行(即BSD类型的unix)。

想法? [注意,先前问题的先前答案不是单行。]

2 个答案:

答案 0 :(得分:2)

您可以动态地将结果附加到文件中,这样您就不需要构建50GB阵列(我假设您没有内存!)。此命令将连接字符串中每个不同索引的连接字段,该字符串将写入以相应索引命名的文件,并带有一些后缀。

编辑:根据OP的评论内容可能包含空格,我建议使用-F"|"代替sub,并且以下答案旨在写入标准输出

(新)代码:

# split the file on the pipe using -F
# if index "i" is still $1 (and i exists) concatenate the string
# if index "i" is not $1 or doesn't exist yet, print current a
# (will be a single blank line for first line)
# afterwards, this will print the concatenated data for the last index
# reset a for the new index and take the first data set
# set i to $1 each time
# END statement to print the single last string "a"
awk -F"|" '$1==i{a=a"|"$2}$1!=i{print a; a=$2}{i=$1}END{print a}' 

这会在给定索引中构建一个“数据”字符串,然后在索引更改时将其打印出来并开始在新索引上构建下一个字符串,直到结束...重复...

答案 1 :(得分:0)

sed '# label anchor for a jump
   :loop
# load a new line in working buffer (so always 2 lines loaded after)
   N
# verify if the 2 lines have same starting pattern and join if the case
   /^\(\([^|]\)*\(|.*\)\)\n\2/ s//\1/
# if end of file quit (and print result)
   $ b
# if lines are joined, cycle and re make with next line (jump to :loop)
   t loop
# (No joined lines here)
# if more than 2 element on first line, print first line
   /.*|.*|.*\n/ P
# remove first line (using last search pattern)
   s///
# (if anay modif) cycle (jump to :loop)
   t loop
# exit and print working buffer
   ' YourFile
  • posix版本(可能是Mac上的--posix)
  • 自我评论
  • 假设已排序的条目,没有空行,数据中没有管道(也没有转义)
  • 使用unbufferd -u进行流程处理(如果可用)