回答:
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.
あなたの質問の例では、両方tacとgrep使用してファイル全体を処理する必要が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
tac、私のポイントは-m、ファイルを2つのプログラムで完全に読み取る必要があるので、あなたも使用しない限り助けにならないということです。それ以外の場合は、すべての出現を検索して、最後に出現したものだけを保持することができますtail -n 1。
grep -m、非常に効率的です。
grep -m場合。OPは使用し-mていないため、grepとtacの両方がすべてを処理していました。
awk行の意味を詳しく説明していただけますか?
理由
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でない場合はsed、awk代わりにまたはを使用できます。しかし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があります。
注意定期的(シーク可能)ファイルに対して、その、tacとtail -r彼らは後方にファイルを読み取る行うので、彼らはちょうどそれが後方に印刷する前にメモリに完全にファイルを(として読んでいない効率的@ SLMのsedのアプローチやtac非正規のファイルになります) 。
どちらtacもtail -r利用できないシステムでは、次のようなプログラミング言語で逆読みを手動で実装することが唯一のオプションですperl。
grep -e "$pattern" file | tail -n1
または:
sed "/$pattern/h;$!d;g" file
しかし、それらはすべての一致を見つけ、最後のものだけを印刷することを意味します。
パターンの最初の出現場所を最後から見つける可能性のある解決策は次のとおりです。
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
を使用して@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 -e 'print reverse <>' file | grep -m 1 $pattern
$ perl -e 'print reverse <>' file | grep -m 1 5
5
sed1つ)grep 5 | tail -n1またはの数桁遅い可能性がありますsed '/5/h;$!d;g'。また、潜在的に多くのメモリを使用します。まだGNUを使用しているので、移植性はそれほど高くありませんgrep -m。