ファイルに表示される3つの単語を繰り返し検索します。

ファイルに表示される3つの単語を繰り返し検索します。

私のEメールバックアップで重要なEメールを検索しようとしています。何千ものファイルを含むサブディレクトリを含むディレクトリ.eml(Linuxファイルシステムの場合)。.eml3つの単語を含むテキストファイルを検索し、1つの単語を除外したいと思います。

まず、ある単語を検索してから、パイプを介して別の単語を検索してみます。

grep -R 'foo' ~/Directory/path | grep 'bar'

これは、同じ行に2つの単語を含むファイルのみを返すため、機能しません。ファイル全体に2つの単語を含むファイルが必要です。

私は単語を含むファイルを見つけて、ファイルの内容を出力ファイルにリンクしようとしています。

grep -rIlZ  '.' -e 'foo' | xargs -0 cat > MyOutputFile 

文脈を見ることができるので役に立ちます。ただし、複数の単語を検索する必要があります。複数の単語を検索し、1つを除外するようにこれを拡張できますか?

ベストアンサー1

foobarand が含まれているファイル名が欲しいとします。いいえ baz。この場合:

find . -type f -exec gawk '
  BEGINFILE{a=b=c=0}
  /foo/{a=1} /bar/{b=1} /baz/{c=1;nextfile}
  ENDFILE{if(a && b && !c)print FILENAME}' {} +

[Linuxを使用しているので、すでにGNU awk(gawk)にアクセスできるとします。 ]

このアプローチでは、できるだけ少ない数の awk 呼び出しが開始され、各ファイルは一度だけ読み取られます。中間ファイルは必要ありません。これはうまくいきます。

はい

2つのファイルを含むディレクトリを考えてみましょう。

$ cat file1.eml 
foo and
bar only
$ cat file2.eml 
foo
and
bar
and
baz

コマンドを実行すると、./file1.eml要件を満たす唯一のファイルが生成されます。

$ find . -type f -exec gawk '
    BEGINFILE{a=b=c=0}
    /foo/{a=1} /bar/{b=1} /baz/{c=1;nextfile}
    ENDFILE{if(a && b && !c)print FILENAME}' {} +
./file1.eml

どのように動作しますか?

  • find一般ファイルのリストを再帰的に収集して渡しますgawk

  • BEGINFILE{a=b=c=0}

    これにより、各新しいファイルの先頭に変数abc0(false)に設定されます。

  • /foo/{a=1}

    行にが含まれている場合、foo変数はa1に設定されます。 (本物)。

  • /bar/{b=1}

    行にが含まれている場合、bar変数はb1に設定されます。 (本物)。

  • /baz/{c=1;nextfile}

    行にが含まれている場合、baz変数はc1に設定されます。 (本物)。

    除外したい単語を見つけたら(baz例のように)、ファイルをもう読む必要はありません。だから私たちはnextfile残りの行をスキップし、すぐにENDFILEに移動します。

  • ENDFILE{if(a && b && !c)print FILENAME}

    各ファイルの終わりに ifabいいえ c(awk の論理!- ではない) 両方が true の場合、ファイル名を印刷します。

GNUではなくawk

たとえば、awkに良いBEGINFILE機能がない場合は、各ファイルに対して1つずつ実行する必要があります。ENDFILEmawkawk

find . -type f -exec mawk '
  /foo/{a=1} /bar/{b=1} /baz/{c=1;exit}
  END{if(a && b && !c) print FILENAME}' {} \;

または(ヒント:エドモートン):

awk 'FNR==1 { if (a && b && !c) print fname; fname=FILENAME; a=b=c=0 } /foo/{a=1} /bar/{b=1} /baz/{c=1}   END{if(a && b && !c) print FILENAME}' *.eml

または再帰検索を使用してください。

find . -type f -exec awk 'FNR==1 { if (a && b && !c) print fname; fname=FILENAME; a=b=c=0 } /foo/{a=1} /bar/{b=1} /baz/{c=1}   END{if(a && b && !c) print FILENAME}' {} +

おすすめ記事