用 awk for 循环替换特定行中的值

用 awk for 循环替换特定行中的值

我想将第一行、第三行、第五行等所有字段中的值 3 和 4 分别替换为 0 和 1,直到以下数据集的末尾:

 2 4 3 0
 2 4 3 0
 3 0 4 4
 3 0 4 4
 4 2 4 3
 4 2 4 3
 2 3 4 2
 2 3 4 2

所以,期望的结果是:

 2 1 0 0
 2 4 3 0
 0 0 1 1
 3 0 4 4
 1 2 1 0
 4 2 4 3
 2 0 1 2
 2 3 4 2

我正在使用以下代码来执行此操作:

awk '{for (i = 1; i <= NR; i=(i+2)) 
    if($i == 3) {$i = 0}
    if($i == 4) {$i = 1} 
}
END {print $0}' b.temp

但是,此代码的输出仅为 b.temp 文件最后一行中的值 (2 3 4 2)。

我怎样才能做到这一点?该代码需要针对任意数量的行和字段。解决方案可以是 awk、sed 或 shell 脚本中的其他替代方案。

提前致谢

答案1

使用 sed:

sed 'y/34/01/;n' file

意思是:

  • 将这一行中的3和4替换为0和1并打印;
  • 获取下一行并打印它;
  • 获取下一行并重复循环。

但是,如果数据包含(例如 14),则将其转换为 11,这将失败。要解决此问题,请选择

sed 's/\<4\>/1/g;s/\<3\>/0/g;n' file

这些\<and\>匹配单词的开头和结尾。

答案2

您的方法的问题在于您在块print中只有一个语句END。该块中的值$0是文件最后一行的内容。因此,您的awk代码只会打印文件的最后一行。

另请注意,将awk操作应用于每行除非块之前有条件{ ... }(例如END仅适用于文件结尾)。所以,你的代码会尝试检查第一个、第三个等场地每行如果是3or分别用or4替换...但永远不要打印结果。01

为了将规则应用于每个奇数行,您可以检查是否NR%2为一(或简单地非零):

awk 'NR%2{for (i=1;i<=NF;i++) if ($i==3 || $i==4) $i-=3}1' b.temp

1出现在规则之外时, 是awk“打印执行的所有转换所产生的行”的简写符号。

答案3

下面是我尝试的更优化的命令

命令

awk 'NR==1||NR==3||NR==5||NR==7{gsub("3","0",$0)}1' file | awk 'NR==1||NR==3||NR==5||NR==7{gsub("4","1",$0)}1'

输出

 2 1 0 0
 2 4 3 0
 0 0 1 1
 3 0 4 4
 1 2 1 0
 4 2 4 3
 2 0 1 2
 2 3 4 2

相关内容