如何根据从第二个文件中获取的值范围排除文件中的行

How to exclude lines in a file based on a range of values taken from a second file

我有一个包含值范围列表的文件:

2    4
6    9
13   14

第二个文件如下所示:

HiC_scaffold_1  1   26
HiC_scaffold_1  2   27
HiC_scaffold_1  3   27
HiC_scaffold_1  4   31
HiC_scaffold_1  5   34
HiC_scaffold_1  6   35
HiC_scaffold_1  7   37
HiC_scaffold_1  8   37
HiC_scaffold_1  9   38
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  13  39
HiC_scaffold_1  14  39
HiC_scaffold_1  15  42

我想从文件 2 中排除第 2 列的值在文件 1 定义的范围内的行。理想的输出是:

HiC_scaffold_1  1   26
HiC_scaffold_1  5   34
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  15  42

我知道如何使用 awk 提取单个范围:

awk ' == "2", == "4"' file2.txt

但是我的文件 1 有很多范围值(行),我需要排除而不是提取与这些值对应的行。

这是一个错误:

$ awk '
NR==FNR {                           # first file
    min[NR]=                      # store mins and maxes in pairs
    max[NR]=
    next
}
{                                   # second file
    for(i in min)                   
        if(>=min[i]&&<=max[i])
            next
}1' ranges data

输出:

HiC_scaffold_1  1   26
HiC_scaffold_1  5   34
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  15  42

如果范围不是很大并且是整数值但数据很大,您可以制作一个值的排除映射以加速比较:

$ awk '
NR==FNR {                       # ranges file
    for(i=;i<=;ex[i++]);    # each value in the range goes to exclude hash
    next
}
!( in ex)' ranges data        # print if not found in ex hash

催眠

如果file2.txt的第二列始终等于其行的索引,则可以使用sed修剪这些行。如果这不是您的情况,请参阅 awkception 段落。

sed $(sed 's/^\([0-9]*\)[[:space:]]*\([0-9]*\)/-e ,d/' file1.txt) file2.txt

其中 file1.txt 包含您的范围,file2.txt 是数据本身。

基本上它构建了一个 sed 调用,链接了一个 -e i,jd 表达式列表,这意味着它将删除 ith 行和 第j行.

在您的示例中,sed 's/^\([0-9]*\)[[:space:]]*\([0-9]*\)/-e ,d/' file1.txt 将输出 -e 2,4d -e 6,9d -e 13,14d,这是用于在 file2.txt.

上调用 sed 的表达式列表

最后会调用:

sed -e 2,4d -e 6,9d -e 13,14d file2.txt

此命令删除第2和第4之间的所有行,第6和第9之间的所有行,以及第13和第14之间的所有行。

显然,如果 file2.txt 的第二列 匹配其自身行的索引,则它不起作用。

awkception

awk "{$(awk '{printf "if (>=%d && <=%d) next\n", , }' file1.txt)}1" file2.txt

即使第二列与其所在行的索引不匹配,此解决方案也有效。

该方法使用 awk 创建一个 awk 程序,就像 sedsedception 中创建 sed 表达式一样解决方案。

最后这将调用:

awk '{
if (>=2 && <=4) next
if (>=6 && <=9) next
if (>=13 && <=14) next
}1' file2.txt

需要注意的是这个解决方案比sed慢很多。

如果您的范围不大:

$ cat tst.awk
NR==FNR {
    for (i=; i<=; i++) {
        bad[i]
    }
    next
}
!( in bad)

$ awk -f tst.awk file1 file2
HiC_scaffold_1  1   26
HiC_scaffold_1  5   34
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  15  42