「バー」が10行以内に表示されない「フー」のインスタンスを検索するgrep


10

「Foo」が発生するすべてのCPPファイルをツリー全体で検索するとします。私はするかもしれません:

find . -name "*.cpp" | xargs grep "Foo"

ここで、前の結果の3行以内に「Bar」が発生しないという他の文字列があるインスタンスのみをリストしたいとします。

したがって、2つのファイルが与えられます。

a.cpp

1 Foo
2 qwerty
3 qwerty

b.cpp

1 Foo
2 Bar
3 qwerty

a.cppからの "Foo"は検出されますが、b.cppからの "Foo"は検出されない単純な検索を作成したいと思います。

これをかなり簡単な方法で達成する方法はありますか?


多分解決策はオプションgrep -Aおよび/またはgrep -Bおよび/またはgrep -Cである可能性があります。私は努力していますが、
成功

@ maurelio79:私の現在の理論はこれです。コンテキストに-A 10を使用した "Foo"のGrep。それをgrep -v Barにパイプします。sedにパイプして、ファイル名と行番号を取得します。それを(何か?)にパイプして、その行を印刷します。
John Dibling 2014年

回答:


17

pcregrep

pcregrep --include='\.cpp$' -rnM 'Foo(?!(?:.*\n){0,2}.*Bar)' .

キーは、複数の行に-M固有でpcregrepあり、複数の行を照合するために使用されるオプションにあります(pcregrepREがそれを要求するときに、必要に応じて入力ファイルからより多くのデータを引き出します)。

(?!...)perl / PCREの否定先読みRE演算子です。Foo(?!...)試合はFoo限り...以下のものと一致していません。

...されている(?:.*\n){0,2}.*Bar.含まれている行に続く0〜2行である、改行文字と一致しません)Bar


+1:すばらしい。本当にありがとう; 正しい正規表現を理解するのは簡単ではなかったと思います。皆さんの努力に大変感謝しています。これは私が望んだとおりに機能しているようです。
John Dibling 2014年

2
あなたが答えようとするなら、副問。どのようにして知りましたpcregrepか?今まで聞いたことがありません。
John Dibling 2014年

@JohnDibling、私は個人的に最近見つけたunix.SEに。そのREは、特に(?!...)否定的な先読みperlRE演算子に慣れている場合は特に複雑ではありません。
ステファンChazelas

9

決して心は、単に使用pcregrepとして提案 @StephaneChazelasで。


これはうまくいくはずです:

$ find . -name "*.cpp" | 
    while IFS= read -r file; do 
      grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
    done 

アイデアは、grepの-Aスイッチを使用して、一致した行とそれに続くN行を出力することです。次に、a grep Barを介して結果を渡し、それが一致しない場合(exit> 0)、ファイルの名前をエコーし​​ます。

正しいファイル名がある(スペース、改行、その他の奇妙な文字がない)ことがわかっている場合は、次のように簡略化できます。

$ for file in $(find . -name "*.cpp"); do 
   grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
  done 

例えば:

terdon@oregano foo $ cat a.cpp 
1 Foo
2 qwerty
3 qwerty
terdon@oregano foo $ cat b.cpp 
1 Foo
2 Bar
3 qwerty
terdon@oregano foo $ cat c.cpp 
1 Foo
2 qwerty
3 qwerty
4 qwerty
5. Bar
terdon@oregano foo $ for file in $(find . -name "*.cpp"); do grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; done 
./c.cpp
./a.cpp

c.cppを含む行がから3行を超えBarBarいるため、が含まれているにもかかわらずが返されることに注意してくださいFoo。に渡される値を変更して、検索する行数を制御します-A

$ for file in $(find . -name "*.cpp"); do 
   grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done 
./a.cpp

これは短いものです(使用する場合bash):

$ shopt -s globstar 
$ for file in **/*cpp; do 
    grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done

重要

Stephane Chazelasがコメントで指摘したように、上記のソリューションはまったく含まFooれていないファイルも印刷します。これはそれを回避します:

for file in **/*cpp; do 
  grep -qm 1 Foo "$file" && 
  (grep -A 3 Foo "$file" | grep -q Bar || echo "$file"); 
done

きちんとした+1。私が望んでいたよりも少し複雑ですが、まったく悪くはありません。
John Dibling 2014年

これは、「Foo」が1回だけ発生することを前提としています。これは、を含まないファイルも報告しますFoo。引用符がありません。
ステファンChazelas

@StephaneChazelasありがとう、引用を修正しました。no Fooを使用してファイルをレポートすることについてはあなたはまったく正しいと私はそれを修正しましたが、の複数のインスタンスについてのあなたの要点はわかりませんFoo。それらを正しく処理する必要があります。
terdon

@JohnDibling更新を参照してください。
terdon

1
100行の "Foo"の後に "Bar"が続くファイルは報告されません。
ステファンChazelas

0

未テスト、私は私の電話にいます:

find . -name "*.cpp" | xargs awk '/foo/{t=$0;c=10}/bar/{c=0;t=""}c{c--}t&&!c{print t;t=""}END&&t{print t}' 

そんな感じ。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.