grep 查找 "Foo" 的实例,其中 "Bar" 未出现在 10 行内

Joh*_*ing 10 grep

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

find . -name "*.cpp" | xargs grep "Foo"
Run Code Online (Sandbox Code Playgroud)

现在假设我想列出只有那些情况下,其他一些字符串,说“酒吧”并没有在3线前一个结果的发生。

所以给定两个文件:

a.cpp

1 Foo
2 qwerty
3 qwerty
Run Code Online (Sandbox Code Playgroud)

b.cpp

1 Foo
2 Bar
3 qwerty
Run Code Online (Sandbox Code Playgroud)

我想构建一个简单的搜索,其中找到 a.cpp 中的“Foo”,但没有找到 b.cpp 中的“Foo”。

有没有办法以相当简单的方式实现这一目标?

Sté*_*las 17

pcregrep

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

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

(?!...)是 perl/PCRE 否定前瞻 RE 运算符。Foo(?!...)匹配Foo,只要...不匹配以下内容。

...(?:.*\n){0,2}.*Bar.不匹配换行符),即 0 到 2 行后跟包含Bar.

  • 如果您愿意回答,则附带问题。您是如何知道“pcregrep”的?我以前从未听说过。 (2认同)

ter*_*don 9

没关系,只需pcregrep按照@StephaneChazelas 的建议使用即可。


这应该有效:

$ find . -name "*.cpp" | 
    while IFS= read -r file; do 
      grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
    done 
Run Code Online (Sandbox Code Playgroud)

这个想法是使用 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 
Run Code Online (Sandbox Code Playgroud)

例如:

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
Run Code Online (Sandbox Code Playgroud)

请注意,c.cpp尽管包含,但仍会返回,Bar因为 withBar在 之后超过 3 行Foo。您可以通过更改传递给的值来控制要搜索的行数-A

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

这是一个较短的(假设您使用bash):

$ shopt -s globstar 
$ for file in **/*cpp; do 
    grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done
Run Code Online (Sandbox Code Playgroud)

重要的

正如 Stephane Chazelas 在评论中指出的那样,上述解决方案还将打印根本不包含的文件Foo。这避免了:

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