入力からawkまでの最後の3行のみを除くすべての行を印刷したい。ファイルにはn行の行が含まれていることに注意してください。
たとえば
file.txt
を含む、
foo
bar
foobar
barfoo
last
line
出力をしたい、
foo
bar
foobar
tac
とsed
またはtac
とawk
の組み合わせで可能になると思います
$ tac file | sed '1,3d' | tac
foo
bar
foobar
$ tac file | awk 'NR==1{next}NR==2{next}NR==3{next}1' | tac
foo
bar
foobar
しかし、私はawkのみを介して出力が必要です。
非常に不格好ですが、配列にすべての行を追加し、最後に(長さがわかれば)最後の3行を除くすべてを出力できます。
... | awk '{l[NR] = $0} END {for (i=1; i<=NR-3; i++) print l[i]}'
別の(より効率的なhere)アプローチは、3つの変数に手動でスタックします。
... | awk '{if (a) print a; a=b; b=c; c=$0}'
a
は、行がc
からb
に移動してからa
に移動した後にのみ出力されるため、3行に制限されます。差し迫った欠点は、すべてのコンテンツをメモリに保存しないことであり、バッファリングの問題を引き起こすことはありません(印刷する場合はfflush()
)、しかしここでの欠点はこれをスケールアップするのが簡単ではないことです。最後の100行をスキップするには、100個の変数と100個の変数ジャグリングが必要です。
Awkに配列用のPush
およびpop
演算子があれば、簡単になります。
または、行数と$(($(wc -l < file) - 3))
を実際に使用する範囲を事前に計算することもできます。これはstreamedコンテンツには比較的役に立たないが、ファイル上ではかなりうまく機能する:
awk -v n=$(($(wc -l < file) - 3)) 'NR<n' file
通常、head
を使用するだけです。
$ seq 6 | head -n-3
1
2
3
terdonのベンチマーク を使用すると、実際にこれらがどのように比較されるかを確認できます。しかし、私は完全な比較を提供すると思いました:
head
:0.018秒(私)awk
+ wc
:0.169s(me)awk
3変数:0.178s(me)awk
ダブルファイル:0.322s(terdon)awk
循環バッファー:0.355s(Scrutinizer)awk
for-loop:0.693s(me)最速の解決策は、head
やwc
などのC最適化ユーティリティを使用することです。しかし、pureawk
では、今のところ手動で回転するスタックが重要です。
メモリ使用量を最小限に抑えるには、循環バッファーを使用できます。
awk 'NR>n{print A[NR%n]} {A[NR%n]=$0}' n=3 file
行番号にmod演算子を使用すると、最大n個の配列エントリがあります。
N = 3の例を取り上げます。
1行目NR%n
は1に等しく、2行目は2を生成し、3行目は0を生成し、4行目は再び1に評価されます。
Line 1 -> A[1]
Line 2 -> A[2]
Line 3 -> A[0]
Line 4 -> A[1]
Line 5 -> A[2]
...
4行目に到達すると、A[NR%n]
には1行目のコンテンツが含まれます。したがって、印刷され、A[NR%n]
に4行目のコンテンツが取得されます。次の行(5行目)最後まで印刷するなど。まだ印刷されていないのは、最後の3行を含むバッファーの内容です...
anythingをメモリに保持しないように、ファイルを2回処理することもできます。
awk '{if(NR==FNR){c++}else if(FNR<=c-3){print}}' file file
ここでのトリックは、NR==FNR
テストです。 NR
は現在の行番号で、FNR
は現在のファイルの現在の行番号です。複数のファイルが入力として渡される場合、FNR
は、最初のファイルが処理されている間のみNR
と等しくなります。この方法で、最初のファイルの行数をすばやく取得し、c
として保存します。 「2つの」ファイルは実際には同じファイルなので、必要な行数がわかったので、これがそのうちの1つである場合にのみ印刷します。
これは他のアプローチよりも遅いと思うかもしれませんが、処理がほとんど行われないため、実際には高速です。すべては、単一の算術比較とは別に、内部awk
ツール(NR
およびFNR
)を使用して行われます。このコマンドで作成された100万行の50MBファイルでテストしました。
for i in {500000..1000000}; do
echo "The quick brown fox jumped over the lazy dog $i" >> file;
done
ご覧のとおり、時間はほぼ同じですが、私がここで提供したアプローチは、Oliの最初の提案がわずかに高速です(ただし、他の方法よりも低速です)。
$ for i in {1..10}; do (
time awk '{if(NR==FNR){c++}else if(FNR<=c-3){print}}' file file > /dev/null ) 2>&1 |
grep -oP 'real.*?m\K[\d\.]+';
done | awk '{k+=$1}END{print k/10" seconds"}';
0.4757 seconds
$ for i in {1..10}; do (
time awk '{l[NR] = $0} END {for (i=1; i<=NR-3; i++) print l[i]}' file > /dev/null ) 2>&1 |
grep -oP 'real.*?m\K[\d\.]+';
done | awk '{k+=$1}END{print k/10" seconds"}';
0.5347 seconds
質問はawk
に関するものでしたが、簡潔にするために常に使用できます。
head -n -3