#!/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
ループが実行されると、PATTERN
1、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
。