web-dev-qa-db-ja.com

特定の行の値をawk forループに置き換えます

最初の行、3番目の行、5番目の行のすべてのフィールドで、次のデータセットの終わりまで、値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、またはその他の方法で作成できます。

前もって感謝します

2
andrec

Sedを使用:

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

つまり:

  • この行の3と4を0と1に置き換えて印刷します。
  • 次の行を取得して印刷します。
  • 次の行を取得してサイクルを繰り返します。

ただし、データにたとえば14が含まれている場合は、11に変換すると失敗します。これを回避するには、

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

これらの\<および\>は、単語の最初と最後に一致します。

5
Quasímodo

このアプローチの問題は、printブロックにENDステートメントが1つしかないことです。そのブロックの$0の値は、ファイルの最終行の内容です。したがって、awkコードはファイルの最後の行のみを出力します。

また、{ ... }ブロックの前に条件がない場合(awkがファイルの終わりにのみ適用されるなど)を除き、ENDは操作をevery lineに適用することに注意してください。したがって、コードは1、3などをチェックしようとしますフィールドすべての行であり、それが3または4である場合、それぞれ0または1で置き換えます...しかし、決して結果を印刷します。

すべての奇数行にルールを適用するために、NR%2が1(または単にゼロ以外)かどうかを確認できます。

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

1は、ルールの外で発生する場合、「実行されたすべての変換から得られた行を出力する」ためのawk省略表記です。

5
AdminBee

より最適化されたコマンドは以下にあります私の試みです

コマンド

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
0