awkを使用して最後の行を最初に処理する


11

awk最後のデータポイントに基づいて、を使用して正規化するデータファイルがあります。そのため、私は最初に最後のデータポイントにアクセスし、データを正規化してから、正常に処理したいと考えています。

次の方法は、tac2回使用するとうまくいきますが、おそらく必要以上に複雑です。

$ cat file
0 5
1 2
2 3
3 4
$ tac file | awk 'NR==1{norm=$2} {print $1, $2/norm}' | tac
0 1.25
1 0.5
2 0.75
3 1

私の質問は次のとおりです。awkのみを使用して上記の結果を取得することは可能ですか?

答えは「いいえ、awkはファイルを1行ずつスキャンします」と思いますが、代わりの方法を提案します。

回答:


5

あなたはawkの2パスソリューションとしてそれを行うことができます:

awk 'FNR == NR { n = $2; next } { print $1, $2/n }' infile infile

awkのバージョンがENDFILEブロックをサポートしている場合(たとえば、GNU awk 4+)、次のように実行できます。

awk 'ENDFILE { n = $2 } FNR != NR { print $1, $2/n }' infile infile

seekファイルの最後まで最初にcamhの回答を見る方が効率的であることに注意してください。

説明

最初の例は前のを思い出すことで機能します$2。つまり、ローカルラインカウンター(FNR)がグローバルラインカウンター(NR)と等しい場合にのみ評価されます。nextコマンドは、次の行にスキップし、この場合には、第2引数が解析されるときに、最後のブロックにのみ評価されることを保証します。

2番目の例にも同様のロジックがありますが、入力ファイルの終わりに達したときに評価されるENDFILEブロックを利用しています。


最初の例は正常に動作しますが、2番目は動作しません$ awk --version GNU Awk 3.1.8。2つの入力ファイルがどのように処理され、何nextが行われるかについて、非常に小さな説明を追加できますか?
Bernhard、

1
@Bernhard:編集を参照
トール

6

データソースが複数回読み取ることができるファイルである場合(つまり、ストリームではない場合)、最初にを使用tail(1)して、最終行から必要なデータを取得し、それをファイルの順次処理のためにawkに渡す必要があります。tailその前のすべてのデータを読み取る必要なく、ファイルの最後までシークして最終行を読み取ります。

awk -v norm=$(tail -n 1 file | cut -d' ' -f2) '{print $1, $2/norm}' file

これは、ファイル全体がバッファキャッシュに収まらない(つまり、パスごとに1回ずつ、ディスクから2回読み取る必要がある)大きなファイルでは大きな利益となり、スキャンする必要がないため、ある程度は役立ちます。最後の行に到達するための入力。ファイルが小さい場合、2パスアプローチとの違いはそれほど大きくありません。


3

あなたはそれらを配列にロードして逆に読むことができます:

awk '{x[i++]=$0} END{for (j=i-1; j>=0;) print x[j--] }'

あなたはそれをより効率的に行うことができますが、この種のことはなぜawkこれが適切なツールではないのかを示しています。tac可能な場合は引き続き使用してください。一般に、GNU tacは、この仕事のためのさまざまなツールの中で最速です。


- forループを使用するawkことは解決策ではないことに同意します。
Bernhard、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.