在awk中根据特定列打印指定值范围内的所有行

时间:2016-04-10 20:37:47

标签: awk

输入数据基于第2列进行排序,如下所示:

1   100
1   101
1   200
3   360
4   800
4   950
4   952

使用示例数据,所需输出为:

1   200 3   360
4   800 4   950
4   800 4   952

也就是说,如果第2列中有值的行在范围内:value2大于value1 + 100&& value2小于value1 + 200。

我的尝试是:

awk 'BEGIN{FS="\t"; PREVLOC=$2; PREVLINE=$0}{ if($2>PREVLOC+200 || $2<PREVLOC+100 {PREVLOC=$2; PREVLINE=$0;} else {print PREVLINE"\t"$0; PREVLOC=$2; PREVLINE=$0;} }' inputfile

将前一行和上一行第2列保存到变量中以进行比较。但是,它并不适用于所有情况。使用示例数据,它不会打印最后一对。如果在它们之间存在一条线,其中第二列值例如是,则它也不会输出800-950对。 890。

目前,我已经用bash完全不同的方式解决了这个问题:

`while read var1 var2; do stuff with vars in awk; done<inputfile`

但它很慢。非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

我不知道这对你有多大的改进,因为它仍然是O(n^2)算法,但它都是awk并值得一试。

有两个通行证。 NR==FNR块是第一次传递并将整个文件读入内存(如果文件非常大,则另一个可能的问题,如果您担心性能,我猜它很大)。对于每一行,我们在第二遍中存储要测试的范围。

第二次传递逐行进行并扫描每组完整范围以找到符合条件的范围。

请注意,在调用awk时,您需要在命令行上提供两次输入文件,如图所示。

$ cat input.txt
1   100
1   101
1   200
3   360
4   800
4   950
4   952

$ cat b.awk
# first pass, load array with ranges
NR==FNR {range[$0] = ($2 + 100) ":" ($2 + 200); next}

# Here we process the file for the second time, looping through
# all ranges for every line of input
{
    for (i in range) {
        split(range[i], r, ":")
        if ($2 > r[1] && $2 < r[2]) {
            print i, $0
        }
    }
}

$ awk -f b.awk input.txt input.txt
1   200 3   360
4   800 4   950
4   800 4   952