我有一个包含值范围列表的文件:
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 == "2", $2 == "4"' file2.txt
但是我的文件1有很多范围值(行(,我需要排除而不是提取与这些值对应的行。
这是一个错误:
$ awk '
NR==FNR { # first file
min[NR]=$1 # store mins and maxes in pairs
max[NR]=$2
next
}
{ # second file
for(i in min)
if($2>=min[i]&&$2<=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=$1;i<=$2;ex[i++]); # each value in the range goes to exclude hash
next
}
!($2 in ex)' ranges data # print if not found in ex hash
如果您的范围不是很大:
$ cat tst.awk
NR==FNR {
for (i=$1; i<=$2; i++) {
bad[i]
}
next
}
!($2 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
seception
如果file2.txt
的第二列总是等于其行的索引,则可以使用sed
修剪行。如果您的情况并非如此,请参阅awkception一段。
sed $(sed 's/^([0-9]*)[[:space:]]*([0-9]*)/-e 1,2d/' file1.txt) file2.txt
其中file1.txt
包含您的范围,file2.txt
是数据本身。
基本上,它构造了一个sed
调用,该调用链接-e i,jd
表达式的列表,这意味着它将删除第i行和第j行之间的行。
在您的示例中,sed 's/^([0-9]*)[[:space:]]*([0-9]*)/-e 1,2d/' 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 ($2>=%d && $2<=%d) nextn", $1, $2}' file1.txt)}1" file2.txt
即使第二列与其行的索引不匹配,此解决方案仍然有效。
该方法使用awk
创建awk
程序,就像sed
在seception解决方案中创建sed
表达式一样。
最后,这将调用:
awk '{
if ($2>=2 && $2<=4) next
if ($2>=6 && $2<=9) next
if ($2>=13 && $2<=14) next
}1' file2.txt
应该注意的是,该解决方案明显慢于sed
。