使用 awk 命令根据文件中的列值删除行

使用 awk 命令根据文件中的列值删除行

我有如下所示的巨大文件。如果第二列的值为 60、30 等,我想删除这些行,所有这些值都将从逗号分隔的另一个文件中获取。

position_id risk_measure_id Scenario_id value_usd
1   60  0   300.8
2   30  0   400.6
3   45  90  300.7
4   60  0   200.9
5   30  9   400.8
6   60  10  4000.9
8   20  0   5000.9

我可以使用下面的awk命令来实现它,但如果我有多个值需要排除,是否有任何简单的方法。

$ awk '{ if ($2!=60 && $2!=25 && $2!=30) print $0}' test.txt
position_id risk_measure_id Scenario_id value_usd
3   45  90  300.7
8   20  0   5000.9

答案1

将您的值放入另一个文件中:

values

60
25
30

然后将它们读入数组中awk

awk 'FNR == NR {arr[$0] = 1; next} !($2 in arr)'  values test.txt

FNR == NR在读取第一个文件时成立,因此第一个块仅在读取值时执行。由于next,因此!($2 in arr)仅对第二个文件执行。

答案2

您的命令可以进一步简化 - 您不需要if语句和代码块,因为 awk 可以使用代码块前面的匹配条件打印行。如果您只想打印行,则可以完全跳过代码块:

$ awk '$2!=60 && $2!=25 && $2 != 30'  input.txt        
position_id risk_measure_id Scenario_id value_usd
3   45  90  300.7
8   20  0   5000.9

替代解决方案是使用数组:

awk -v values="60 30 25" 'BEGIN{split(values,array)};{ flag=0; for(val in array) if (array[val] == $2) flag=1; if (flag==0) print }'  input.txt

实际情况是,我们创建一个包含所有所需值的字符串,并以空格分隔。在 BEGIN 语句中,我们将其分解为数组。主代码块在读取每一行时将标志变量设置为 0,然后我们循环遍历数组中的所有值并检查字段 #2 是否与数组中的任何内容匹配。如果匹配,我们将标志设置为 1。循环退出后,我们查看循环是否找到任何内容并设置标志,如果没有找到 - 打印该行。

此方法的较短版本是,如果找到排除值,则使用next命令中断循环。这样,print只有未找到排除值时才会达到函数:

awk -v values="60 30 25" 'BEGIN{split(values,array)};{for(val in array) if (array[val] == $2) next; print}'  input.txt 

相关内容