私のEメールバックアップで重要なEメールを検索しようとしています。何千ものファイルを含むサブディレクトリを含むディレクトリ.eml
(Linuxファイルシステムの場合)。.eml
3つの単語を含むテキストファイルを検索し、1つの単語を除外したいと思います。
まず、ある単語を検索してから、パイプを介して別の単語を検索してみます。
grep -R 'foo' ~/Directory/path | grep 'bar'
これは、同じ行に2つの単語を含むファイルのみを返すため、機能しません。ファイル全体に2つの単語を含むファイルが必要です。
私は単語を含むファイルを見つけて、ファイルの内容を出力ファイルにリンクしようとしています。
grep -rIlZ '.' -e 'foo' | xargs -0 cat > MyOutputFile
文脈を見ることができるので役に立ちます。ただし、複数の単語を検索する必要があります。複数の単語を検索し、1つを除外するようにこれを拡張できますか?
答え1
foo
bar
and が含まれているファイル名が欲しいとします。いいえ 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}
これにより、各新しいファイルの先頭に変数
a
とb
がc
0(false)に設定されます。/foo/{a=1}
行にが含まれている場合、
foo
変数はa
1に設定されます。 (本物)。/bar/{b=1}
行にが含まれている場合、
bar
変数はb
1に設定されます。 (本物)。/baz/{c=1;nextfile}
行にが含まれている場合、
baz
変数はc
1に設定されます。 (本物)。除外したい単語を見つけたら(
baz
例のように)、ファイルをもう読む必要はありません。だから私たちはnextfile
残りの行をスキップし、すぐにENDFILEに移動します。ENDFILE{if(a && b && !c)print FILENAME}
各ファイルの終わりに if
a
とb
いいえc
(awk の論理!
- ではない) 両方が true の場合、ファイル名を印刷します。
GNUではなくawk
たとえば、awkに良いBEGINFILE
機能がない場合は、各ファイルに対して1つずつ実行する必要があります。ENDFILE
mawk
awk
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
追加:一致が一致しません。grep
word
someword
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
で終わりますfoo
。bar
いいえ 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
役に立ったことを願っています:)