grep查找“ Foo”的实例,其中“ Bar”没有出现在10行之内


10

假设我要在整个树中搜索出现“ Foo”的所有CPP文件。我可以这样做:

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

现在,假设我列出那些在上一个结果的三行之内没有出现其他字符串(例如“ Bar”)的实例。

因此,给出两个文件:

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中。我想,但没有sucess ....
maurelio79

@ maurelio79:我目前的理论是这样。使用-A 10表示上下文的“ Foo”的Grep。用管道将其传递给grep -v Bar。用管道传输要获取文件名和行号的sed。用管道输送到(某物?)以打印该行。
John Dibling 2014年

Answers:


17

pcregrep

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

该键位于该-M选项中,该选项唯一pcregrep且用于匹配多行(pcregrep在需要RE时,根据需要从输入文件中提取更多数据)。

(?!...)是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,如果不匹配(退出> 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尽管包含,但仍返回,Bar因为with Bar所在的行多于3行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”仅发生一次。这还将报告不包含的文件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}' 

这样的事情。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.