我有一个像这样的文件,它是制表符分隔的:
name v1 v2 v3 v4
g1 4.5 2.3 2.1 0.2
g2 10 3 5 2.3
g3 7 2.5 2.8 3.9
只是向您展示了一个虚拟文件,其中有 5 列和 4 行(包括标题)。我想过滤掉行,如果特定行中的每一列的值 >= 2,则保留该行,否则将其删除。输出应如下所示:
name v1 v2 v3 v4
g2 10 3 5 2.3
g3 7 2.5 2.8 3.9
我该如何使用 awk 来做到这一点?
答案1
AFAIK awk 没有办法显式地迭代字段。例如:
$ awk 'NR>1 {for(i=2;i<=NF;i++) if($i+0 < 2) next} 1' file
name v1 v2 v3 v4
g2 10 3 5 2.3
g3 7 2.5 2.8 3.9
答案2
Steeldriver 已经提供了 awk 解决方案。这是一个 perl 版本(使用数组切片而不是 for 循环):
$ perl -lane 'print if ($.==1 || grep ($_ >= 2, @F) == $#F)' input.txt
name v1 v2 v3 v4
g2 10 3 5 2.3
g3 7 2.5 2.8 3.9
这仅打印第一行(标题)以及所有数字字段的值大于或等于 2 的行。(非数字字段如g1
或g2
将计算为0
)
注意:perl的功能在概念上类似,但与命令行程序grep()
并不完全相同。grep
grep(expression,array)
$_ >= 2
在其第一个参数(例如)中运行表达式数组的每个元素(例如@F
),并返回一个由结果为 true 的每个元素组成的数组。
在标量上下文中(例如与整数进行数字比较),它返回表达式为 true 的次数,而不是数组。这就是我们在这里所做的== $#F
,以测试与$#F
(数组中元素的数量@F
)的等价性
该表达式可以是本示例中使用的简单测试,也可以是包含任何 Perl 代码的代码块。它还可以选择修改每个元素。例如,@new = grep(s/foo/bar/g, @old)
将使用 @old 中已成功修改的所有元素填充 @new(即至少包含一个“foo”的 thost。所有这些元素都更改为“bar”)。perldoc -f grep
详情请参阅。