回答:
tac
grep -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バイト)が蓄積されるまで書き込みを行いません。
つまり、grep
8192バイトのデータを書き込む前に終了しないので、おそらくかなりの数の行があります。
GNU grep
では、--line-buffered
which を使用して、端末に行くかどうかに関係なく行が見つかったらすぐに書き込むように指示することで、より早く終了させることができます。その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
sed
1つ)grep 5 | tail -n1
またはの数桁遅い可能性がありますsed '/5/h;$!d;g'
。また、潜在的に多くのメモリを使用します。まだGNUを使用しているので、移植性はそれほど高くありませんgrep -m
。