ファイルのスペースを含む Grep 文字列

ファイルのスペースを含む Grep 文字列
#!/bin/bash
LIST=/errors_exception.txt
cd /test
for PATTERN in `cat $LIST`
do
        for FILE in $(ls)
        do
        if zcat $FILE | grep -Fxq "$PATTERN"; then
        echo "$PATTERN found pattern in $FILE" >> output
        fi
done
done

多数の圧縮ログファイル(.gz)をスキャンし、私が探しているパターンがこのログにまだ存在することを確認しようとしています。

たとえば、上記のコードにerrors_exception.txt次のものが含まれているとします。

one 
one two three
four five
six

/test- ディレクトリにはログファイルが含まれています。

スクリプトを実行するときに2行目の「one two three」を1行で読み取らないのはなぜですか?

bash -x test.sh(スクリプト名)を実行すると、テキストファイルに他の3行があるかのように2行目を読み、「one two three」を1行で表示します。

ベストアンサー1

list=/errors_exception.txt
cd /test
while IFS= read -r pattern ; do
    for file in * ; do
        if zcat < "$file" | grep -Fxq "$pattern"; then
            echo "$pattern found pattern in $file"
        fi
    done
done <"$list" > output

メモ:

  • 次の2行のどれも期待どおりに機能しません。

    for PATTERN in `cat $LIST`
    
    for FILE in $(ls)
    

    どちらの場合も、シェルは予期しない単語分離を実行します。上記の提案されたコードはこの状況を防ぎます。

  • ファイルはerrors_exception.txt実際にはルートディレクトリにありますか?

  • 変数を小文字に変換します。これはユーザーが作成した変数のルールです。このルールは、特定の重要なシェルパラメータを誤って無視するのを防ぎます。

単語分割に関する追加情報

シェルが実行されるとき:

for PATTERN in `cat $LIST`

それは働くcat $LIST。これにより、空白、タブ、およびキャリッジリターンはすべてハイフン接続と同じものとして扱われます。したがって、実際にトークン化した後、この行は次のようになります。

for PATTERN in one one two three four five six

そしてforループが実行されると、PATTERN1、1、2、3、4、5、6の順に割り当てられます。

実際に望むのは、各行を行として扱うことです。これがwhile read.... done<"$list"まさにこの構成が使用される理由です。各ループから行全体を読みます。

ファイル名にスペースが含まれている場合、この行でも同じ問題が発生します。

for FILE in $(ls)

結果はls行に置き換えられ、ファイル名にスペース、タブ、またはキャリッジリターン(すべて有効な文字)が含まれている場合、名前は複数の部分に分割されます。たとえば、空のディレクトリにファイルを作成します。

$ touch "a b c"

それではforループを実行してみてください。

$ for file in $(ls); do echo $file; done
a
b
c

forファイルが1つしかない場合でも、ループは3回実行されます。これは、ファイル名にスペースが含まれており、単語の分離後にforループが3つのパラメータa、b、cを取得するためです。

これは避けやすいです。代わりに使用してください:

for file in *

シェルは、含まれている文字に関係なく、すべてのファイル名をそのまま維持できるほどスマートです。

再帰検索

サブディレクトリからgzip圧縮ファイルも検索するには、次のようにbashのglobstar機能を使用できます。

list=/errors_exception.txt
cd /test
shopt -s globstar
while IFS= read -r pattern ; do
    for file in **/*.gz ; do
        if zcat < "$file" | grep -Fxq "$pattern"; then
            echo "$pattern found pattern in $file"
        fi
    done
done <"$list" > output

これは必要ですbash

おすすめ記事