ディレクトリの指定されたファイル名でのみパターン/テキストを再帰的に検索しますか?


16

私は(例えば、ディレクトリを持っているabc/def/efg多くのサブディレクトリ(例えば,:で)abc/def/efg/(1..300))。これらのサブディレクトリにはすべて共通のファイル(例:)がありますfile.txtfile.txt他のファイルを除いて、これだけで文字列を検索したい。これどうやってするの?

を使用しましたが grep -arin "pattern" *、サブディレクトリとファイルが多数ある場合は非常に遅くなります。


回答:


21

親ディレクトリでは、これらのファイルのみを使用findして実行できgrepます。

find . -type f -iname "file.txt" -exec grep -Hi "pattern" '{}' +

2
に渡すことをお勧め-Hします。grepこれにより、パスが1つだけ渡された場合でも、そのパスが(ファイルの一致する行だけでなく)印刷されます。
エリアケイガン

24

globstarを使用することもできます。

建物grepでコマンドをfindZannaの答えのように、(も参照これを行うには非常に堅牢で、汎用性、および移植可能な方法であるsudodusの答えを)。そしてmuruは、使用しての優れたアプローチ掲載しているgrep--includeオプションを。ただし、grepコマンドとシェルだけを使用する場合は、別の方法があります。シェル自体に必要な再帰を実行させることができます

shopt -s globstar   # you can skip this if you already have globstar turned on
grep -H 'pattern' **/file.txt

-Hフラグ作るには、grep一つだけ一致するファイルが見つかった場合でも、ファイル名を示しています。あなたが渡すことができ-a-iおよび-nフラグ(あなたの例から)へのgrepそれはだ場合にも、あなたが必要なもの。しかし、合格しない-rか、-Rこのメソッドを使用している場合。それは、シェルグロブパターン含む拡大にディレクトリを再帰的**、そしてませんがgrep

これらの手順は、Bashシェルに固有のものです。BashはUbuntu(および他のほとんどのGNU / Linuxオペレーティングシステム)のデフォルトのユーザーシェルです。したがって、Ubuntuを使用していて、シェルが何であるかわからない場合は、ほぼ確実にBashです。一般的なシェルは通常、ディレクトリトラバース**グロブをサポートしていますが、常に同じように機能するとは限りません。詳細については、ステファンChazelas優れた答え*** LS **とLS、LS *の結果をUnix.SE

使い方

globstar bash シェルオプションをオンにすると**、ディレクトリセパレーター(/)を含むパスに一致します。したがって、これはディレクトリを再帰するグロブです。具体的には、man bash次のとおりです。

ときglobstarのシェルオプションが有効になっている、と*パス名展開のコンテキストで使用され、隣接する二つの*は、すべてのファイルと0個以上のディレクトリおよびサブディレクトリに一致する単一のパターンとして使用するのです。/が後に続く場合、2つの隣接する*はディレクトリとサブディレクトリのみに一致します。

特に**を意図したときに書き込む場合は、意図したよりもはるかに多くのファイルを変更または削除するコマンドを実行できるため、これには注意が必要です*。(このコマンドでは安全でshopt -u globstar、ファイルは変更されません。)globstarシェルオプションをオフに戻します。

globstarとにはいくつかの実際的な違いがありfindます。

findglobstarよりもはるかに多用途です。globstarでできることは何でも、findコマンドでもできます。私はglobstarが好きで、時にはもっと便利ですが、globstarはの一般的な代替手段ではありませんfind

上記のメソッドは、名前が.。で始まるディレクトリ内を検索しません。そのようなフォルダを再帰したくない場合もありますが、そうする場合もあります。

通常のグロブと同様に、シェルは一致するすべてのパスのリストを作成しgrep、グロブ自体の代わりにコマンド()に引数として渡します。呼び出さfile.txtれたファイルが非常に多く、結果のコマンドが長すぎてシステムを実行できない場合、上記の方法は失敗します。実際には、(少なくとも)何千ものそのようなファイルが必要ですが、それは起こる可能性があります。

使用するメソッドはfind、次の理由によりこの制限を受けません。

  • Zannaの方法は、grep潜在的に多くのパス引数を使用してコマンドをビルドおよび実行します。しかし、単一のパスにリストできるよりも多くのファイルが見つかった場合、+-terminated -execアクションは、いくつかのパスでコマンドを実行し、さらにいくつかのパスでコマンドを実行します。以下の場合、grep複数のファイル内の文字列のためのINGのは、これは正しい動作を生成します。

    ここで説明するglobstarメソッドのように、これは一致するすべての行を出力し、それぞれにパスを付加します。

  • sudodusの方法は、検出grepされたそれぞれに対して個別に実行されますfile.txt。多くのファイルがある場合、他の方法よりも遅いかもしれませんが、動作します。

    このメソッドはファイルを見つけてパスを出力し、続いて一致する行があればそれを出力します。これは、私のメソッドであるZanna'sおよびmuru'sによって生成された形式とは異なる出力形式です。

色をつける find

globstarを使用する直接の利点の1つは、Ubuntuのデフォルトでは、grepカラー化された出力を生成することです。しかし、これも簡単に取得できfindます

Ubuntuのユーザーアカウントは、実際に実行する(表示するために実行する)エイリアスを使用して作成されます。エイリアスは、インタラクティブ発行する場合にのみ展開されるは良いことですが、フラグで呼び出す場合は、明示的に記述する必要があります。例えば:grepgrep --color=autoalias grepfindgrep--color

find . -name file.txt -exec grep --color=auto -H 'pattern' {} +

bashこれを機能させるにはシェルを使用する必要があることをより明確に述べたい場合があります。「グロブスターbashシェルオプション」では暗黙のうちにそれを言っています、速すぎて読む人には簡単に見落とされる可能性があります。
スティグヘマー

多くの批判的なコメントを引き起こしたため、回答を削除しました。したがって、あなたはあなたの答えからそれへの参照を削除すべきです。
sudodus

@StigHemmerありがとう-すべてのシェルにこの機能があるわけではないことを明確にした。多くのシェル(だけでなく、bashは)サポートディレクトリ・トラバースんが**グロブを、あなたのコアの批判は正しいです:のプレゼンテーション**この答えでは唯一のshoptいるbashのとでは、bashに固有の用語「globstar」である(と思う)はbashとtcshのみ。もともとこれらの複雑さのためにこれについて説明しましたが、あなたはそれがやや紛らわしいことは正しいです。この回答で詳細に説明するのではなく、私は重いリフティングを行う別の(非常に徹底的な)投稿にリンクしました。
エリアケイガン

@sudodus私はそうしましたが、これが一時的なものであることを願っています。私と他の人は、あなたの答えが貴重だと感じました。-eパスに適用すべきではないのは事実ですが、これは簡単に修正できます。最初のコマンドについては、単に省略し-eます。2番目の場合、find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;またはを使用しますfind . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;。ユーザーは-e一致する行ごとに1つのパスを出力する他の方法よりも(使用方法を固定して)あなたの方法を好む場合があります。yoursは、見つかったファイルごとに1つのパスを出力し、その後にgrep結果を出力します。
エリアケイガン

@sudodusしたがって、grepそれ自体あなたがしていることをしません。他の批判も間違っていました。grep -Hrun by -execはなしでは色付けしません--color(またはGREP_COLOR)。IEEE 1003.1-2008では{}拡張が保証されていません##### {}:が、UbuntuにはGNU findがあります。よろしければ、投稿を編集して-eバグを修正し(そしてユースケースを明確にします)、削除を取り消すかどうかを確認します。(削除された投稿を表示/編集する担当者がいます。)
エリアカガン

18

findこれは必要ありません。grepこれを単独で完全に処理できます:

grep "pattern" . -airn --include="file.txt"

からman grep

--exclude=GLOB
      Skip  files  whose  base  name  matches  GLOB  (using   wildcard
      matching).   A  file-name  glob  can  use  *,  ?,  and [...]  as
      wildcards, and \ to quote  a  wildcard  or  backslash  character
      literally.

--exclude-from=FILE
      Skip  files  whose  base name matches any of the file-name globs
      read from FILE  (using  wildcard  matching  as  described  under
      --exclude).

--exclude-dir=DIR
      Exclude  directories  matching  the  pattern  DIR from recursive
      searches.

--include=GLOB
      Search  only  files whose base name matches GLOB (using wildcard
      matching as described under --exclude).

ニース-これが最良の方法のようです。シンプルで効率的。この方法について知っていた(またはマンページを確認しようと思っていた)ことを望みます。ありがとう!
エリアケイガン

@EliahKagan Zannaがこれを投稿しなかったことにもっと驚いています-少し前に別の答えのためにこのオプションの例を示しました。:)
ムル

2
遅い学習者、悲しいかな、しかし、私は最終的にそこに着きます、あなたの教えは私に完全に無駄にされていません;)
Zanna

これは非常にシンプルで覚えやすいです。ありがとうございました。
ラジェシュケラディマス

これが最良の答えであることに同意します。混乱を減らすために回答を削除するか、代替案があること、および何ができるかを示すためにそのままにしてfind?
おいてください-sudodus

8

多くの場合muruの回答に記載されている、ファイル名を指定するフラグを指定grepして実行する方法--includeが最良の選択です。ただし、これはでも実行できfindます。

この回答のアプローチでは、見つかった各ファイルに対して個別findに実行grepし、各ファイルに見つかった一致する行の上に、各ファイルへのパスを1回だけ出力します。(一致するすべての行の前にパスを印刷する方法は、他の回答で説明されています。)


これらのファイルがあるディレクトリツリーの最上部にディレクトリを変更できます。次に実行します:

find . -name "file.txt" -type f -exec echo "##### {}:" \; -exec grep -i "pattern" {} \;

これは、.という名前の各ファイルのパス(現在のディレクトリに対する相対パス、およびファイル名自体を含む)を出力しfile.txt、その後にファイル内のすべての一致する行が続きます。これ{}は、見つかったファイルのプレースホルダーであるため機能します。各ファイルのパスは#####、接頭辞としてを付けることで内容とは別に設定され、そのファイルの一致する行の前に一度だけ印刷されます。(file.txt一致するものを含まないファイルのパスは印刷されます。)この出力は、一致するすべての行の先頭にパスを印刷するメソッドから得られるものよりも整然としています。

正しい名前のファイルを検索し、他のすべてのファイルをスキップするため、findこのように使用すると、ほとんどの場合grepすべてのファイルで実行するよりも高速になります(grep -arin "pattern" *find

UbuntuはGNUの検索使用し常に拡大し{}、それが長い文字列で表示された場合でも同様に、##### {}:。これをサポートしない可能性のあるシステムでコマンドを使用findする必要がある場合、または-exec絶対に必要な場合にのみアクションを使用する場合は、次を使用できます。

find . -name "file.txt" -type f -printf '##### %p:\n' -exec grep -i "pattern" {} \;

出力を読みやすくするために、 ANSIエスケープシーケンスを使用して色付きのファイル名を取得できます。これにより、各ファイルのパス見出しは、その下に印刷される一致する行から目立ちやすくなります。

find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;

それはあなたのシェルが原因オンにするエスケープコードの端末に緑の生成、実際のエスケープシーケンスに緑のために、通常の色のエスケープコードで同じことをやって。これらのエスケープはに渡されfind、ファイル名を出力するときに使用されます。($' 'ここでは、ANSIエスケープコードの解釈に対してfind'の-printfアクションが認識さ\eれないため、引用が必要です。)

ご希望の場合は、代わりに使用することができ-exec、システムのprintfコマンド(サポートしています\e)。同じことをする別の方法は次のとおりです。

find . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;

私は配列を使用して「forループ」を作成するつもりでしたが、findのexecネイティブオプションについては考えませんでした。いいね!しかし、ドットを使用すると、すでにあなたがいるディレクトリであなたを見つけると思います。間違っている場合は修正してください。検索順序で直接解析するように指定する方が良いと思いませんか?find abc/def/efg -name "file.txt" -type f -exec echo -e "##### {}:" \; -exec grep -i "pattern" {} \;
kcdtv

確かに、cd abc/def/efg「ディレクトリの変更」コマンドは削除されます:-)
sudodus

(1)なぜ-eオプションを指定するのechoですか?これにより、バックスラッシュを含むファイル名が破損します。(2)引数の一部{}としての使用、機能することが保証されていません。-exec echo "#####" {} \;またはと言う方が良いでしょう-exec printf "##### %s:\n" {} \;。(3)-printまたはを単に使用しないのはなぜ-printfですか?(4)も考慮してくださいgrep -H
G-Manが「Reinstate Monica」と言う

@ G-man、1)私は元々ANSIカラーを使用していたため:find . -name "file.txt" -type f -exec echo -e "\0033[32m{}:\0033[0m" \; -exec grep -i "pattern" {} \;2)あなたは正しいかもしれませんが、今のところこれは私のために働いています。3)-printおよび-printfも選択肢です。4)これはすでに主要な回答にあります。-とにかく、あなた自身の答えを歓迎します:
sudodus

2つの-exec呼び出しは必要ありません。使用するだけでgrep -H、ファイル名(色)と一致したテキストが印刷されます。
テルドン

0

質問の条件を文学的にとることができる場合、直接grepを使用できることを指摘するだけです。

grep 'pattern' abc/def/efg/*/file.txt

または

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