ファイルに表示される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}' {} +

答え2

find -exec以下を試してくださいgrep -q

find /my/path -name "*.eml" \
  -exec grep -F -q "word1" {} \; \
  -exec grep -F -q "word2" {} \; \
  -exec grep -F -q "word3" {} \; \
  ! -exec grep -F -q "word4" {} \; \
  -print
  • grep -qステータスコードのみを返す
  • 単語以外のパターンを検索するには、以下-Fを省略してください。grep
  • 単語全体を一致させるには-w追加:一致が一致しません。grepwordsomeword
  • findコマンドを接続し-exec、そのいずれかが失敗した場合は停止します(grep -qエラーコードが返された場合)。

答え3

次の方法を使用できます。

grep -rIlZe foo . |
  xargs -r0 grep -lZe bar |
  xargs -r0 grep -LZe baz |
  xargs -r0 cat > MyOutputFile

grepつまり、最初に生成されたファイルのリストがxargs -r0次のファイルのリストに供給され、リストがさらにgrep具体化されます。

-L最後のオプションは、一致するものがないレポートファイルgrepと似ているため、次を含むファイル-lで終わりますfoobarいいえ baz

-rまたは-I最初の項目のみが必須ですgrep。後者は、再帰するディレクトリではなく通常のファイルのリスト(バイナリファイルは-I最初のファイルでフィルタリングされますgrep)を引数として使用します。r

これは、ファイルの内容を何度も読み取ることができることを意味し、これはあまり効率的ではありませんが、通常はgrep実装がはるかに高速であり、awk上記の4つのコマンドはすべて並列に実行されるため、いくつかの処理が複数回実行されます。プロセッサは同時に実行され、データはすでにメモリにキャッシュされているため、awk次のものに基づいているプロセッサよりも高速になる可能性があります。

答え4

このコードをコピーして新しいbashスクリプトファイルに貼り付け、ターミナルで保存してchmod +x <file>実行すると、以下を含むすべてのファイルが一覧表示されます。「金持ち」そして「バー」そして含まない「海賊」ひも:

#!/bin/bash
function notcontain {
        for FILE in $(find . 2> /dev/null); do
                if ! grep "rab" $FILE > /dev/null 2>&1; then
                        echo $FILE
                fi
        done
}
    
for FILE in `notcontain`; do
        if grep "foo" $FILE > /dev/null 2>&1 | grep "bar" $FILE > /dev/null 2>&1; then
                echo $FILE
        fi
done

役に立ったことを願っています:)

関連情報