2つの異なる単語(順序、行)があるかどうかをテキストファイルを検索します。

2つの異なる単語(順序、行)があるかどうかをテキストファイルを検索します。

私は同じファイルに2つの単語インスタンスがあるかどうかを調べる方法を探しています。これまでの検索を実行するには、以下を使用しました。

find . -exec grep -l "FIND ME" {} \;

私が経験している問題は、「FIND」と「ME」の間にスペースがないと検索結果からファイルが生成されないことです。 「FIND ME」の代わりに「FIND」と「ME」の両方が存在するファイルで、事前検索文字列をどのように調整できますか?

私はAIXを使用しています。

答え1

GNUツールの使用:

find . -type f  -exec grep -lZ FIND {} + | xargs -r0 grep -l ME

標準的には次のことができます。

find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;

grepただし、これによりファイルごとに最大2つが実行されます。あまりにも多くgrepを実行せずにファイル名に文字を許可しながら移植性を維持するには、次のようにします。

convert_to_xargs() {
  sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
    {
      if (NR > 1) {
        printf "%s", line
        if (!index($0, "//")) printf "\\"
        print ""
      }
      line = $0
    }'
    END { print line }'
}

export LC_ALL=C
find .//. -type f |
  convert_to_xargs |
  xargs grep -l FIND |
  convert_to_xargs |
  xargs grep -l ME

アイデアは、xargsの出力をfindxargsに適した形式に変換することです(ここでは、空白(ロケールの場合はSPC / TAB / NL C、他のロケールのYMMV)で区切られた単語のリストが必要です。ここで、一重引用符、二重引用符、バックスラッシュはスペースをエスケープできます。

通常の出力は後処理できませんfind -print。なぜなら、ファイル名を改行で区切ってファイル名の改行をエスケープしないからです。たとえば、次のような場合があります。

./a
./b

b呼び出されたディレクトリから1つのファイルを呼び出すのか、a<NL>.現在のディレクトリから2つのファイルを呼び出すのかわかりません。ab

を使用すると、ファイルパスに出力として表示できない.//.ため(空の名前を持つディレクトリは存在せず、ファイル名には許可されていないため)が含まれている行が表示された場合は、次のことがわかります。 new ファイル名の最初の行です。したがって、このコマンドを使用して、この行の前の行を除くすべての改行をエスケープできます。//find///awk

上記の例を取ると、find出力は最初のケース(ファイル1つ)になります。

.//a
./b

awkは次にエスケープされます。

.//a\
./b

したがって、これはxargs引数と見なされます。 2番目のケース(2つのファイル):

.//a
.//b

これはawkそのままですので、xargs両方のパラメータを参照してください。

任意のバイトシーケンスで作業するには(ユーザーロケールで有効な文字を形成しなくても)、単純化するために(そしていくつかの実装)LC_ALL=Cが必要です。sedawkxargsスペースSPCとTABのみを定義し、バックスラッシュを含むエンコードされた文字を異なる方法で解釈するさまざまなユーティリティの問題を回避します。

答え2

ファイルが単一のディレクトリにあり、名前にスペース、タブ、改行、または文字が含まれていない、または*で始まらない場合は、?MEを含むファイルのリストを取得し、FINDも含めるように範囲を狭めます。[-.

grep -l FIND `grep -l ME *`

答え3

awk以下も実行できます。

find . -type f  -exec awk 'BEGIN{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; END{if (cx > 0 && cy > 0) print FILENAME}' {} \;

cxと を使用して、cyそれぞれ一致する行数を計算します。そのブロックの両方のカウンタがゼロより大きい場合、印刷はより速く効率的です。FINDMEENDFILENAME
gnu awk

find . -type f  -exec gawk 'BEGINFILE{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; ENDFILE{if (cx > 0 && cy > 0) print FILENAME}' {} +

答え4

許可された答えを見ると、必要以上に複雑に見えます。 GNUバージョンはNULLで終わる文字列をサポートしてfindいます。とても簡単です:grepxargs

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l ME

コマンドを変更してfindファイルをフィルタリングできます。これは、解析に複雑さを追加することなく、すべての文字を含むファイル名に対して機能しますsed。ファイルをさらに処理するには、--null最後に別のファイルを追加してください。grep

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l --null ME | xargs -0 echo

そして関数として:

find_strings() {
    find . -type f -print0 | xargs -0 grep -l --null "$1" | xargs -0 grep -l "$2"
}

明らかに、これらのツールを実行しているGNUバージョンがない場合は、許可された答えを使用してください。

関連情報