如何在Bash中基于起始/结束值消除文件中的重叠条目?
移除制表符文件中与已保留行重叠的条目
我有一个制表符分隔的文件,条目包含名称、起始位置和结束位置,文件内容如下:
# Name Start End Name1 1 3 Name2 7 9 Name3 5 8 Name4 5 6
我希望删除与之前已保留条目存在重叠的行,本示例的期望输出为:
Name1 1 3 Name2 7 9 Name4 5 6
我目前写了部分Bash脚本:
#!/bin/bash while IFS=$'\n' read line; do # Assign variable names name=$(echo $line | cut -f 1) start=$(echo $line | cut -f 2) end=$(echo $line | cut -f 3) # I envision an if statement structured so that: # if [ $end < $PreviousStart ] || [ $start > $PreviousEnd ] ; then echo $line >> output.txt done < file.txt
但这里遇到了问题:我需要检查output.txt里的每一行(也就是原文件中已保留的所有前序行),只有当前行和所有已保留行都不重叠时才打印该行。我觉得Awk应该有更简洁的解决办法,求帮忙。
Awk解决方案
用Awk可以直接在内存中维护已保留的条目,不需要频繁读写文件,效率更高。核心逻辑是遍历每一行,跳过注释行;对当前行的起始/结束位置,和所有已保留条目逐一对比,只要和任意一个重叠就跳过,否则保留该行并记录到内存中。
#!/usr/bin/awk -f BEGIN { FS = "\t" } # 跳过注释行 /^#/ { next } { # 标记当前行是否可保留 keep = 1 # 遍历所有已保存条目,检查重叠 for (i in saved) { split(saved[i], prev, "\t") prev_start = prev[2] prev_end = prev[3] # 区间重叠判断:当前起始 <= 已存结束 且 当前结束 >= 已存起始 if ($2 <= prev_end && $3 >= prev_start) { keep = 0 break } } if (keep) { print $0 saved[++count] = $0 } }
使用方法
- 将上述代码保存为
remove_overlaps.awk - 执行命令:
awk -f remove_overlaps.awk 你的输入文件名.txt > output.txt
逻辑说明
BEGIN { FS = "\t" }:设置字段分隔符为制表符/^#/ { next }:跳过以#开头的注释行- 用数组
saved存储所有已保留的行,新行进来时逐一对比重叠情况 - 重叠判断规则:两个区间
[a1,a2]和[b1,b2]重叠的条件是a1 <= b2 && a2 >= b1,满足则标记为不保留 - 仅当当前行与所有已保存行都不重叠时,才打印并保存该行
内容的提问来源于stack exchange,提问作者Dody
相关产品推荐
相关产品推荐

