AWK技术需求:基于指定范围重叠合并两个文件
使用AWK合并两个存在范围重叠的文件
嘿,这个需求我太熟了!基于区间重叠的文件合并,用AWK来实现既灵活又高效,完全不用写复杂的脚本。咱们先把需求再明确一遍,避免理解偏差:
文件A的每一行格式是:
ID A起始 A结束 附加内容(比如1 10 20 aa)
文件B的每一行格式是:B起始 B结束 附加内容(比如10 15 xx yy mm)
我们需要把A中**[$2,$3]区间和B中[$1,$2]区间**存在重叠的行合并到一起。
方法一:小文件场景(内存足够)
如果你的文件B不算特别大,直接把整个B文件读入内存是最简单的方案,逻辑清晰,容易调试。
AWK脚本实现
# 第一步:读取文件B,把每行的关键信息存入数组 NR == FNR { # 存储B行的起始、结束位置 b_start[NR] = $1 b_end[NR] = $2 # 提取从第3个字段开始的所有附加内容 b_rest[NR] = substr($0, index($0, $3)) next # 跳过后续处理,继续读下一行B } # 第二步:处理文件A的每一行 { a_start = $2 a_end = $3 # 遍历所有B行,检查区间是否重叠 for (i in b_start) { # 区间重叠的核心判断条件:A的起始 <= B的结束,且A的结束 >= B的起始 if (a_start <= b_end[i] && a_end >= b_start[i]) { # 这里可以自定义输出格式,比如输出A整行 + B的附加内容 print $0, b_rest[i] } } }
运行命令
把上面的代码保存为merge.awk,然后执行:
awk -f merge.awk fileB fileA
或者直接用一行命令搞定(不用存脚本):
awk 'NR==FNR{b_start[NR]=$1;b_end[NR]=$2;b_rest[NR]=substr($0,index($0,$3));next}{a=$2;e=$3;for(i in b_start)if(a<=b_end[i]&&e>=b_start[i])print $0,b_rest[i]}' fileB fileA
方法二:大文件场景(内存有限)
如果文件B特别大,把它全存进内存会占用太多资源,这时候我们可以先对两个文件按区间排序,再用归并算法处理,这样不用加载整个文件到内存,效率更高。
步骤1:先排序文件
先对A和B按区间的起始/结束位置排序:
# 对文件A按第2、3字段(A的起始、结束)数值排序 sort -k2,3n fileA > sortedA # 对文件B按第1、2字段(B的起始、结束)数值排序 sort -k1,2n fileB > sortedB
步骤2:归并式AWK脚本
BEGIN { # 先读取第一行B文件的内容 if ((getline b_line < "sortedB") <= 0) { exit # 如果B文件为空,直接退出 } split(b_line, b_fields) b_s = b_fields[1] # B行的起始 b_e = b_fields[2] # B行的结束 b_rest = substr(b_line, index(b_line, b_fields[3])) # B行的附加内容 } # 处理排序后的A文件的每一行 { a_s = $2 a_e = $3 a_full_line = $0 # 跳过所有不可能和当前A行重叠的B行(B的结束 < A的起始) while (b_e < a_s) { if ((getline b_line < "sortedB") <= 0) { b_s = -1 # 标记B文件已读完 break } split(b_line, b_fields) b_s = b_fields[1] b_e = b_fields[2] b_rest = substr(b_line, index(b_line, b_fields[3])) } # 如果B文件已经读完,直接处理下一行A if (b_s == -1) { next } # 遍历所有和当前A行重叠的B行(因为B已排序,连续满足B起始 <= A结束的行都重叠) temp_b_s = b_s temp_b_e = b_e temp_b_rest = b_rest temp_b_line = b_line while (temp_b_s <= a_e) { print a_full_line, temp_b_rest # 读下一行B if ((getline temp_b_line < "sortedB") <= 0) { temp_b_s = -1 break } split(temp_b_line, temp_b_fields) temp_b_s = temp_b_fields[1] temp_b_e = temp_b_fields[2] temp_b_rest = substr(temp_b_line, index(temp_b_line, temp_b_fields[3])) } # 把最后一个未匹配的B行保存下来,供下一个A行使用 if (temp_b_s != -1) { b_line = temp_b_line b_s = temp_b_s b_e = temp_b_e b_rest = temp_b_rest } else { b_s = -1 } }
运行命令
awk -f merge_sorted.awk sortedA
自定义输出格式
如果你不想输出整行,而是需要提取特定字段,只需要修改print语句即可。比如想输出A的ID、A的附加内容、B的附加内容:
# 替换方法一中的print语句 print $1, $4, b_rest[i]
内容的提问来源于stack exchange,提问作者ipj
相关产品推荐
相关产品推荐

