変数に渡されたディレクトリに対してgrepが出力を返さない

変数に渡されたディレクトリに対してgrepが出力を返さない

bash指定されたディレクトリ ツリー内のファイルの内容を検索して、指定された部分文字列が存在するかどうかを確認するスクリプトを作成しようとしています。

の再帰関数だけを使用するのgrepは不十分です。/システムのディレクトリ (およびすべてのサブディレクトリ) を反復処理する必要がある可能性があり、メモリ不足で中断する可能性があります。そのため、スクリプトに渡される引数を示す次の変数をgrep使用して、指定されたディレクトリ ツリー内のすべてのディレクトリとサブディレクトリのリストを取得することにしました。find

searchdir=$HOME     # passed in a script argument
searchstr="secret"  # passed in a script argument

ユーティリティを呼び出してfind、出力を一時ファイルに保存します。

TF=$(mktemp)
find ${searchdir} -type d 1>$TF 2>/dev/null

while-do一時ファイル内のすべてのディレクトリのリストを使用して、各ディレクトリ内のすべてのファイルを検索する目的で、ループを使用してこのファイルの行を反復処理します。 についてはgrep、で提供されるパラメータの形式を使用します。この答え隠しファイルも含め、単一のディレクトリ内のすべてのファイルを検索します。

cat $TF | while read line || [[ -n $line ]];
do
    grepdir="${line}/{*,.*}"
    grep -sHn "${searchstr}" ${grepdir}
done

...ただし、そのコードは出力を生成しません。

確認したところ…

には${TF}すべてのディレクトリの正しいリストが含まれています。${grepdir}変数を出力すると、期待どおりの出力が得られます。

/home/user/{*,.*}
/home/user/.ssh/{*,.*}
/home/user/test/{*,.*}
# ... and so on

grepハードコードされたディレクトリ、特に、~/test/検索対象となる文字列を含む2つのテストファイルを含むディレクトリでコマンドを実行すると、

grep -sHn "${searchstr}" /home/user/test/{*,.*}

... 部分文字列「secret」を含む 2 つのファイルを正しく出力します。

/home/user/test/asdf:7:secret
/home/user/test/test.txt:5:asdfasfdsecretaasdfafd

私にとってうまくいくフォーマットは、再帰的な使用法について議論する回答grep. こうすると:

cat $TF | while read line || [[ -n $line ]];
do
    grep -rn "${line}" -e "${searchstr}"
done

... 出力はいくつか得られますが (技術的には正しいですが、重複するエントリが多数あります)、grepディレクトリを再帰的に処理しており、すべてのディレクトリのリストを持っているため、同じ結果が何度も得られ、前述のルート ディレクトリなどのディレクトリではgrep完全に失敗します。これを回避しようとしています。


$(echo "${grepdir}")また、パラメータとして渡すなど、これを動作させるために必死にハックしたが、やはり結果は出なかったことも言及しておくべきでしょう。

おそらく、 についての私の考え方や理解には誤解があるのでしょう。を呼び出す前に変数を展開すbashべきではないのでしょうか? 私のスクリプトのどこが間違っているのでしょうか?bash${grepdir}grep

答え1

ルール1: コマンドやスクリプトが期待通りに動作しない場合は、 エラーメッセージを確認してください。  に投げ込まないでください /dev/null

次のようなエラーメッセージが表示されます

grep: /home/user/{*,.*}: No such file or directory
grep: /home/user/.ssh/{*,.*}: No such file or directory
grep: /home/user/test/{*,.*}: No such file or directory

しかし、あなたはそれらを見ていません。

見てみるとバッシュ(1)、 私たちは見る

展開は、コマンド ラインが単語に分割された後に実行されます。実行される展開には、中括弧展開、チルダ展開、パラメーターと変数の展開、コマンド置換、算術展開、単語分割、パス名展開の 7 種類があります。

展開の順序は、中括弧展開、チルダ展開、パラメータと変数の展開、算術展開、コマンド置換(左から右に実行)、単語分割、パス名展開です。

あなたの状況で重要なのは、括弧の展開が変数の展開の前に行われることです。つまり、

grep -sHn "${searchstr}" "${line}"/{*,.*}

それから

  • 括弧展開により、最後のトークンが および に変換され"${line}"/*ます"${line}"/.*
  • /home/user/*変数展開により、上記はとなり/home/user/.*、その後
  • パス名を展開すると、上記はファイル名のリストに変換されます。

しかし、あなたが言うには

grep -sHn "${searchstr}" ${grepdir}

それから

  • 変数展開により最後のトークンが になります/home/user/{*,.*}

そして、括弧の展開が発生するには遅すぎます。  grep文字通り と呼ばれるファイルを探します/home/user/{*,.*}


追伸

grep -sHn "${searchstr}" "${line}/{*,.*}"

引用符によって中括弧の展開とパス名の展開が妨げられるため、これも機能しません。

PPS これらすべてのブレースは必要ありません。

grep -sHn "$searchstr" "$line"/{*,.*}

大丈夫でしょう。

答え2

システム全体を再帰的に処理するときに grep が中止する理由は、データ量に対応できなかったからではなく、/proc、/sys、または /dev 内のいずれかの疑似ファイルまたはデバイス ファイルに遭遇したためである可能性があります。--excludeコマンド ラインのオプションを使用して、問題のあるディレクトリを除外できます。

ワイルドカードが展開されない理由は、ワイルドカードが次の行で引用されているからです。

    grepdir="${line}/{*,.*}"

これをこのように変更すると、拡張が容易になる可能性があります。

    grepdir="${line}/"{*,.*}

findこれを実現する別の方法 (スクリプトを少なくする) は、ファイル パスを使用してファイルを選択し、処理のためにパイプすることですxargsfind / ... -print 0 | xargs -0 ...

ただし、どちらの方法でも、除外しない限り、元の再帰 grep が遭遇したファイルは遭遇する可能性があります。

関連情報