繰り返しパターンを検索し、一致する各項目の特定のシーケンス(行番号、ファイル名、ファイル内容なし)を印刷します。

繰り返しパターンを検索し、一致する各項目の特定のシーケンス(行番号、ファイル名、ファイル内容なし)を印刷します。

私が望むのはこことほぼ同じですが、結果の形式を「行番号、区切り文字、ファイル名、改行」と指定して、行番号がファイル名の後ろではなく行の先頭に表示されるようにしたいと思います。一致する項目を含む行は表示されません。

この形式が好ましい理由は次のとおりです。

  • (ㅏ)ファイル名は長くて秘密にすることができ、ツールがファイル名を行番号と区切るために使用する区切り文字を含みます。これは、ファイル内のパターンにも同じ区切り文字を含めることができるため、awkでこれを行うのは非常に困難です。また、行の先頭の行番号は、ファイル名の後に表示される行番号よりも整列しています。この必須形式が必要な別の理由は、
  • (二)パターンに一致する行が長すぎるため、stdoutに示されている出力から1行あたり1行の属性を混乱させることができます。ファイルに保存し、次のツールを使用するよりもstdoutで出力を表示する方が良いでしょう。 viを使用すると、出力ファイルの各行を1行ずつ表示できます。

    ディレクトリからパターンを再帰的に検索し、ファイル名と行番号のみを印刷する方法

これでリクエストを行ったので、次の点を考慮してください。

  1. 私が使用しているLinuxホストにAckがインストールされていないため、使用できません。

  2. 次の操作を行うと、シェルはfind ."find ." を実行し、現在の作業ディレクトリから開始し、再帰的に降りる絶対パスのリストに置き換えます。

    grep -n PATTERN $(find .)
    

    その後、-n は行番号を印刷しますが、目的の場所は印刷しません。また、何らかの理由でディレクトリ名にパターンが含まれていると、grepがそのパターンを含む通常のファイルと一致するかどうかはわかりません。これは私が望むものではないので、次を使用します。

    grep -n PATTERN $(find . -type f)
    

    また、 find 出力が grep に動的に渡されるように、このコマンドを変更したいと思います。まず、絶対パスの完全なリストを作成してから、ほとんどをgrepに渡すのではなく、リストを作成するときに各行をgrepに渡したほうがよいので、次のことを試しました。

    find . -exec grep -n PATTERN  '{}' \;
    

    によると、これは正しい構文であるようですが、man pageこのコマンドを実行するとBashシェルが約100倍遅く実行されるため、これは正しいアプローチではありません。

私が説明した内容を考えると、どのようにこのコマンドと同様の操作を行い、必要な形式を取得できますか?問題の投稿に関する質問をリストしました。

答え1

grepを使う

を使用する代わりにファイルシステムの反復に-r切り替えることができないのはなぜですか?私はまた、スイッチの代わりに2つの追加のスイッチを使用します。grepfind-n

$ grep -rHn PATTERN <DIR> | cut -d":" -f1-2

例 #1

$ grep -rHn PATH ~/.bashrc | cut -d":" -f1-2
/home/saml/.bashrc:25

詳細

  • -r- ファイル+ディレクトリを再帰的に検索
  • -H- 一致する場合はファイル名を印刷します(より制限が少ない)。つまり、他のスイッチと一緒に-l使用されます。grep
  • -n- 一致する行番号を表示

例 #2

$ grep -rHn PATH ~/.bash* | cut -d":" -f1-2
/home/saml/.bash_profile:10
/home/saml/.bash_profile:12
/home/saml/.bash_profile_askapache:99
/home/saml/.bash_profile_askapache:101
/home/saml/.bash_profile_askapache:118
/home/saml/.bash_profile_askapache:166
/home/saml/.bash_profile_askapache:218
/home/saml/.bash_profile_askapache:250
/home/saml/.bash_profile_askapache:314
/home/saml/.bash_profile_askapache:2317
/home/saml/.bash_profile_askapache:2323
/home/saml/.bashrc:25

検索の使用

$ find . -exec sh -c 'grep -Hn PATTERN "$@" | cut -d":" -f1-2' {}  +

はい

$ find ~/.bash* -exec sh -c 'grep -Hn PATH "$@" | cut -d":" -f1-2' {}  +
/home/saml/.bash_profile:10
/home/saml/.bash_profile:12
/home/saml/.bash_profile_askapache:99
/home/saml/.bash_profile_askapache:101
/home/saml/.bash_profile_askapache:118
/home/saml/.bash_profile_askapache:166
/home/saml/.bash_profile_askapache:218
/home/saml/.bash_profile_askapache:250
/home/saml/.bash_profile_askapache:314
/home/saml/.bash_profile_askapache:2317
/home/saml/.bash_profile_askapache:2323
/home/saml/.bashrc:25

本当に使用したい場合は、.findファイルを使用してファイルを見つけて、これを行うことがfindできます。grepfind

答え2

grep -n PATTERN `find . -type f`

これは、コマンド置換の出力がスペースで区切られたファイル名のワイルドカードパターンのリストとして解釈されるため、これはお勧めできません。ファイル名にスペースまたはそのいずれかが含まれている場合、\[*?このスニペットは機能しません。また、一致するファイルが多いとコマンドラインが長すぎます。

find . -exec grep -n PATTERN  '{}' \;

これは素晴らしく安定していgrepますが、ファイルごとに一度呼び出されます。だから、速度が遅すぎます。

-exec … {} +できるだけ多くのファイルを一括実行するコマンドです。最後のバッチ(または理論的に異なるバッチ)は単一のファイルで構成できるため、grepファイル名は印刷されません。-H常にファイル名を印刷するオプションを渡すか、パラメータを追加します(一致するものは含まれていませんが、少なくとも2つは表示されます/dev/null)。grepファイル名)。

find . -type f -exec grep -Hn PATTERN {} +

GNU grep には一致する行番号を印刷するオプションはありませんが、一致する行テキストを印刷するオプションはありません。 sed を使用して一致するテキストを削除し、行番号をファイル名に置き換えることができます。

find . -type f -exec grep -Hn PATTERN {} + | sed 's/^\([^:]*\):\([^:]*\):.*/\2:\1/'

行番号を右に並べ替えるには、awkが私が考えることができる任意の選択肢よりはるかに簡単です。

find . -type f -exec grep -Hn PATTERN {} + | awk -F : '{printf "%8d:%s", $2, $1}'

grepの代わりにawkでマッチングを実行すると、より多くの制御を得ることができます。 Awkはより一般的な解釈言語ツールなので、遅い傾向があります。 1つの利点は、grepの出力があいまいになる可能性があるコロンまたは改行文字を含むファイル名を処理する方法を選択できることです。次のコードスニペットは、awkを使用して埋め込まれたファイル名:(改行も含みますが、これらのファイル名の場合はあいまいな出力を生成します)を取得して処理します。 awkを使用することに注意してください拡張正規表現たとえば、grep -E(マイナーな変更がありますが、grepまたはawkの実装間の変更以上ではありません)。

find . -type f -exec awk '/PATTERN/ {printf "%d:", FNR; print FILENAME}' {} +

関連情報