ファイルを1行ずつ読み、ファイルの最後の場所を覚えておいてください。

ファイルを1行ずつ読み、ファイルの最後の場所を覚えておいてください。

別のファイルからの入力を使用して、ログファイルの特定の行を特定したいと思います。私はそれを行うためにこの小さなコマンドを使用しています:

while read line; do 
    grep "$line" service.log; 
done < input_strings.txt > result.txt

input_strings.txt約50,000の文字列(1行に1つ)があります。私は現在、これらの文字列のそれぞれについて巨大なservice.logファイル(約2,000,000行)を検索しています。

最初の文字列が10,000行に見つかり、input_strings.txtこの行が私のエントリに書き込まれるとします。その後、2番目の文字列が検索されますが、行1から始まります。service.logresult.txtinput_strings.txtservice.logservice.log

で最初の項目の最後の行を見つけることをどのように覚えていますかservice.log?では、2番目の検索実行はどこで開始できますか?

答え1

一致を取得するためにループをまったく使用する必要はありません。単一のコマンドを使用する方がはるかに高速ですgrep

grep -Ff input_strings service.log > results.txt

つまり、質問に記載されている内容を文字通りに実行するには、変数を使用して最後の一致が見つかった行を追跡できます。

LINE_NUMBER=0
while read LINE; do

    # Search for the next match starting at the line number of the previous match
    MATCH="$(tail -n+${LINE_NUMBER} "service.log" | grep -n "${LINE}" | head -n1)";

    # Extract the line number from the match result
    LINE_NUMBER="${MATCH/:*/}";

    # Extract the matching string from the match result
    STRING="${x#*:}";

    # Output the matching string
    echo "${STRING}";

done < input_strings.txt > result.txt

答え2

最初のキーワードを検索し、一致した後に次のキーワードなどを検索し、一致を印刷したいようです。

一方keywords:

foo
bar

そしてdata

bar 0
foo 1
bar 1
foo 2

awkこれを行う必要があるスクリプトは次のとおりです(GNU awkでテスト)。

$ awk 'BEGIN {i = j = 0} NR==FNR { k[i++] = $0; next} 
       $0 ~ k[j] {j++; print $0} j >= i {exit}' keywords data 
foo 1
bar 1

i0から始めて、j最初のファイル(NR==FNR現在のファイルのレコード/行番号を表示されている行の総数と比較)中にキーワードを配列として収集します。次にj、:thキーワードを一致させ、j一致すると印刷して増やします。すべてのキーワードを見つけて終了します。

のように、grepここのキーワードは実際に正規表現パターンですが、awkここでは明らかに正規表現です。固定文字列を検索するには代わりにindex($0, key)を使用してください$0 ~ key


または、キーワードをロードせずに開始するには、次の手順を実行します。

$ awk -vkeyfile=keywords 'BEGIN {getline key < keyfile } 
      $0 ~ key {print $0; if (!getline key < keyfile) exit;}' data
foo 1 
bar 1

これは簡単なはずです。

関連情報