複数のファイルで最後に出現する文字列を見つける


9

文字列の最後の出現を見つけるには、複数のログファイル(過去24時間に生成されたすべてのファイル、すべて同じディレクトリに保存されている)を検索する必要があります。これは私が書いたコマンドです:

find . -mtime 1 | grep fileprefix | xargs grep 'search string' | tail -1

しかし、これは1つのファイルの最後の行のみを返します。これを調整してすべての行を取得する方法に関する提案はありますか?


末尾と最後のgrepを反転させようとしましたか?見つける。-mtime 1 | grep fileprefix | xargsテール-1 | grep 'search string'
Mathieu

回答:


4

GNUファシリティを想定:

find . -mtime -1 -exec bash -c \
'for f; do tac "$f" | grep -m1 fileprefix; done' _ {} +

すでにbashシェルを使用しているので、「bash -c \」の目的を詳しく説明してください。最後に「_ {} +」の目的もあります。
Lokesh

@Lokesh、をfind使用してファイルに対してコマンドを実行できます-exec。を使用してbash -cbashによって検出されたファイルをループしてfind実行するシェルを生成tac .. | grep -m1 fileprefixします
iruvar

文字列フィルタリングをforループで拡張するために、カットコマンド、つまりfor fを含めようとしていました。tac "$ f"を実行する| grep -m1 fileprefix | cut -d '' -f4,7-8しかし、cutコマンドを入力した瞬間に予期しないファイルの終わりエラーが発生します。私が間違っていることを提案していただけますか。
ロケシュ2015

@lokesh、-d" "カットで使用します。単一引用符ではなく二重引用符
iruvar

1
このfindコマンドは、ファイルのプレフィックスをフィルタリングできます。これはgrep必要ありません。検索文字列がこの回答に含まれていないことも驚くべきことです。
Jonathan Leffler、2015

8

すべてが単一のディレクトリにある場合は、次のようにすることができます。

for file in *fileprefix*; do
    grep 'search string' "$file" | tail -1
done

これらが大きなファイルである場合は、を使用tacしてファイルを逆順で印刷し(最後の行を最初に)、次にgrep -m1最初の出現と一致させることで、速度を上げる価値があるかもしれません。これにより、ファイル全体を読み取る必要がなくなります。

for file in *fileprefix*; do
    tac file | grep -m1 'search string'
done

どちらも、に一致するディレクトリがないことを前提としていますfileprefix。存在する場合は、無視できるエラーが表示されます。問題がある場合は、ファイルのみを確認します。

 for file in *fileprefix*; do
    [ -f "$file" ] && tac file | grep -m1 'search string'
 done

ファイル名も印刷する必要がある場合は、-Hgrep呼び出しに追加してください。または、grepサポートしていない場合は、も検索するように伝えます/dev/null。出力は変更されませんが、grep複数のファイルが指定されているため、ヒットごとに常にファイル名が出力されます。

for file in *fileprefix*; do
    grep 'search string' "$file" /dev/null | tail -1
done

「そうすれば、ファイル全体を読み取る必要がなくなります」—ええと?いいえ、grepでファイル全体を読み取ることは避けますが、代わりにファイル全体をtacで読み取ります。一致がファイルの最初と最後のどちらに近いかによって異なりますが、これがより高速になるかどうかははっきりしません。
ジル「SO-停止されて悪」

@ギレスいいえ、ファイル全体も通しませんtac。最初の一致が見つかるとすぐに終了します。832Mのテキストファイルと最後の行にあるパターンでテストしました。grep -m 1 pattern fileツール〜7秒とtac file | grep -m1 patternかかりました0.009
terdon

4
find . ! -name . -prune -mtime 1 -name 'fileprefix*' \
     -exec sed -se'/searchstring/h;$!d;x' {} +

... ファイルsed-s分離オプションとPOSIX をサポートするGNU を使用している場合に機能しますfind

ただし、! -type dorまたは-type f修飾子を追加する必要があります。ディレクトリを読み取ろうとしてもあまり役に立ちません。通常のファイルに範囲をさらに狭めると、パイプまたはシリアルデバイスファイルでの読み取りのハングを回避できます。

ロジックは非常に簡単です- sedその上書きh一致する入力行のコピーと古い領域をsearchstring、その後、d出力から全ての入力ラインが、各入力ファイルの最後のをeletes。最後の行に到達xすると、保持スペースとパターンスペースが変更されます。そのためsearchstring、ファイルの読み取り中に見つかった場合は、最後に発生したものが自動出力されて出力され、それ以外の場合は空白行が書き込まれます。(それが望ましくない場合/./!dは、sedスクリプトの末尾に追加してください)

これによりsed、65kの入力ファイルごとに1 回の呼び出しが行われますARG_MAX。これは非常に高性能なソリューションであり、非常に簡単に実装できます。

ファイル名も必要な場合は、最近のGNU sedを指定して、Fコマンドを使用してそれらを別々の行に書き出すか、または後にプライマリをfind追加することにより、バッチごとに個別のリストで出力することができます。-print+


1

どうですか:

find . -mtime -1 -name "fileprefix*" -exec sh -c \
'echo "$(grep 'search string' $1 | tail -n 1),$1"' _ {} \;

上記は、各ファイルで最後に検索文字列が出現し、その後にコンマの後にそれぞれのファイル名が続く、素晴らしい出力を提供します(エコーの下の "、$ 1"部分を変更して、フォーマットを変更するか、不要であれば削除します)。「file」という名前の接頭辞が付いたファイルで「10」の検索文字列を検索するサンプル出力は次のとおりです。

[dmitry@localhost sourceDir]$ find . -mtime -1 -name "file*" -exec  sh -c 'echo "$(grep '10' $1 | tail -n 1),$1"' _ {} \;
Another data 02 10,./file02.log
Some data 01 10,./file01.log
Yet another data 03 10,./file03.log 

1
find . -mtime 1 -name 'fileprefix*' -exec grep -Hn 'search string' {} + |
    sort -t: -k1,2 -n | 
    awk -F: '{key=$1 ; $1="" ; $2="" ; gsub(/^  /,"",$0); a[key]=$0} 
             END {for (key in a) { print key ":" a[key] }}'

これは、GNU使用grep-H-n、常にファイル名とすべてのマッチの行番号の両方を印刷するオプションが、それはファイル名と行番号でソートし、パイプをAWKに、アレイ内の各ファイル名の最後の試合を格納すると、最終的に印刷しますそれ。

かなり力ずくの方法ですが、機能します。

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