ファイルの末尾から先頭までのGrep


38

約30.000.000行のファイル(Radius Accounting)があり、特定のパターンの最後の一致を見つける必要があります。

コマンド:

tac accounting.log | grep $pattern

私が必要なものを提供しますが、OSが最初にファイル全体を読み取ってからパイプに送信する必要があるため、遅すぎます。

そのため、最後の行から最初の行までファイルを読み取ることができる高速のものが必要です。

回答:


44

tacgrep -m 1(GNUを想定してgrep)を使用しgrepて最初の一致後に停止する場合にのみ役立ちます:

tac accounting.log | grep -m 1 foo

からman grep

   -m NUM, --max-count=NUM
          Stop reading a file after NUM matching lines.  

あなたの質問の例では、両方tacgrep使用してファイル全体を処理する必要がtac無意味のようなものです。

したがって、を使用しない限り、を使用grep -mせずにtac、の出力を解析しgrepて最後の一致を取得します。

grep foo accounting.log | tail -n 1 

別のアプローチは、Perlまたは他のスクリプト言語を使用することです。例(ここで$pattern=foo):

perl -ne '$l=$_ if /foo/; END{print $l}' file

または

awk '/foo/{k=$0}END{print k}' file

1
特定のパターンの最後の一致を見つける必要があるため、tacを使用しています。提案「grep -m1」を使用すると、実行時間は0m0.597sから0m0.007s \ o /になります。みんなありがとう!
ハブナーコスタ14

1
@HábnerCostaどういたしまして。私はあなたがなぜ使用しているのか理解していますtac、私のポイントは-m、ファイルを2つのプログラムで完全に読み取る必要があるので、あなたも使用しない限り助けにならないということです。それ以外の場合は、すべての出現を検索して、最後に出現したものだけを保持することができますtail -n 1
テルドン

6
なぜ「tac [...]はファイル全体を処理する必要がある」と言うのですか?tacが最初に行うことは、ファイルの最後までシークし、最後からブロックを読み取ることです。これは、strace(1)で自分で確認できます。と組み合わせるとgrep -m、非常に効率的です。
CAMH

1
@camhと組み合わせたgrep -m場合。OPは使用し-mていないため、grepとtacの両方がすべてを処理していました。
テルドン

awk行の意味を詳しく説明していただけますか?
ソパラホデアリエレス

12

理由

tac file | grep foo | head -n 1

最初の一致で停止しないのは、バッファリングのためです。

通常、head -n 1行を読み取った後に終了します。したがってgrep、2番目の行を書き込むとすぐに、SIGPIPEを取得して終了する必要があります。

しかし、何が起こるかというと、その出力は端末にgrep送られず、バッファリングされるからです。つまり、十分な量(GNU grepを使用したテストでは4096バイト)が蓄積されるまで書き込みを行いません。

つまり、grep8192バイトのデータを書き込む前に終了しないので、おそらくかなりの数の行があります。

GNU grepでは、--line-bufferedwhich を使用して、端末に行くかどうかに関係なく行が見つかったらすぐに書き込むように指示することで、より早く終了させることができます。そのgrepため、見つかった2行目で終了します。

しかし、grepとにかくGNU を使用すると、-m 1代わりに@terdonが示したように使用できます。これは、最初の一致で終了するので優れています。

あなたgrepがGNU grepでない場合はsedawk代わりにまたはを使用できます。しかしtac 、GNUコマンドであるため、GNU以外のtac場所でシステムを見つけることはできgrepませんgrep

tac file | sed "/$pattern/!d;q"                             # BRE
tac file | P=$pattern awk '$0 ~ ENVIRON["P"] {print; exit}' # ERE

一部のシステムではtail -r、GNUと同じことを行う必要tacがあります。

注意定期的(シーク可能)ファイルに対して、その、tactail -r彼らは後方にファイルを読み取る行うので、彼らはちょうどそれが後方に印刷する前にメモリに完全にファイルを(として読んでいない効率的@ SLMのsedのアプローチtac非正規のファイルになります) 。

どちらtactail -r利用できないシステムでは、次のようなプログラミング言語で逆読みを手動で実装することが唯一のオプションですperl

grep -e "$pattern" file | tail -n1

または:

sed "/$pattern/h;$!d;g" file

しかし、それらはすべての一致を見つけ、最後のものだけを印刷することを意味します。


4

パターンの最初の出現場所を最後から見つける可能性のある解決策は次のとおりです。

tac -s "$pattern" -r accounting.log | head -n 1

これは、-sとの-rスイッチを使用tacします。これらのスイッチは次のとおりです。

-s, --separator=STRING
use STRING as the separator instead of newline

-r, --regex
interpret the separator as a regular expression

ただし、行の先頭とパターンの間のすべてが失われます。
ychaouche

2

sedを使用する

を使用して@Terdonの良い答えにいくつかの代替方法を示すsed

$ sed '1!G;h;$!d' file | grep -m 1 $pattern
$ sed -n '1!G;h;$p' file | grep -m 1 $pattern

$ seq 10 > file

$ sed '1!G;h;$!d' file | grep -m 1 5
5

$ sed -n '1!G;h;$p' file | grep -m 1 5
5

Perlを使用する

おまけとして、ここでは覚えやすいPerlの表記法を少し簡単に示します。

$ perl -e 'print reverse <>' file | grep -m 1 $pattern

$ perl -e 'print reverse <>' file | grep -m 1 5
5

1
これは(特にsed1つ)grep 5 | tail -n1またはの数桁遅い可能性がありますsed '/5/h;$!d;g'。また、潜在的に多くのメモリを使用します。まだGNUを使用しているので、移植性はそれほど高くありませんgrep -m
ステファンシャゼル14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.