web-dev-qa-db-ja.com

awkコマンドを使用してファイル内の列の値に基づいて行を削除する

以下のような巨大なファイルがあります。 2番目の列の値が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
3
prasanth doni

値を別のファイルに入れます。

values

60
25
30

次に、それらをawkの配列に読み取ります。

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

FNR == NRは、最初のファイルを読み取るときにtrueを保持するため、最初のブロックは値の読み取り中にのみ実行されます。 nextのため、!($2 in arr)は2番目のファイルに対してのみ実行されます。

4
muru

Awkはコードブロックの前の一致条件を使用して行を出力できるため、コマンドをさらに簡略化できます。ifステートメントとコードブロックは不要です。行のみを印刷する場合は、コードブロックを完全にスキップできます。

$ 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 
1