如何使用grep查找多行的模式?

Sao*_*obi 190 regex grep

我想找到按顺序有"abc"和"efg"的文件,这两个字符串在该文件的不同行上.例如:包含内容的文件:

blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
Run Code Online (Sandbox Code Playgroud)

应该匹配.

rin*_*rer 203

Grep不足以进行此操作.

在大多数现代Linux系统中都可以使用pcregrep作为

pcregrep -M  'abc.*(\n|.)*efg' test.txt
Run Code Online (Sandbox Code Playgroud)

还有一个更新的pcre2grep.两者都是由PCRE项目提供的.

pcre2grep可通过Mac端口作为端口的一部分用于Mac OS X pcre2:

% sudo port install pcre2 
Run Code Online (Sandbox Code Playgroud)

并通过Homebrew:

% brew install pcre
Run Code Online (Sandbox Code Playgroud)

或者是pcre2

% brew install pcre2
Run Code Online (Sandbox Code Playgroud)

  • @StevenLu`-M, - multiline` - 允许模式匹配多行. (11认同)
  • 请注意.*(\n |.)*等同于(\n |.)*,后者更短.此外,在我的系统上,运行较长版本时会出现"pcre_exec()error -8".所以试试'abc(\n |.)*efg'! (7认同)
  • 在这种情况下,你需要使表达式非贪婪:''abc.*(\n |.)*?efg'` (6认同)
  • `pcregrep`确实使事情变得更容易,但`grep`也会起作用.例如,请参阅http://stackoverflow.com/a/7167115/123695 (6认同)
  • 并且你可以省略第一个`.*` - >`'abc(\n |.)*?efg'`以使正则表达式更短(并且是迂腐的) (4认同)

小智 110

我不确定grep是否可行,但是sed使它非常简单:

sed -e '/abc/,/efg/!d' [file-with-content]
Run Code Online (Sandbox Code Playgroud)

  • 我怀疑如果它有更多的解释,这个答案会有所帮助,在那种情况下,我会再次投票.我知道一点sed,但还不足以使用这个答案在半小时的摆弄之后产生一个有意义的退出代码.提示:正如您之前的评论所示,'RTFM'很少在StackOverflow上获得投票. (46认同)
  • 通过示例快速解释:sed'1,5d':删除1到5之间的行.sed'1,5!d':删除不在1和5之间的行(即保持行之间)然后代替数字,你可以用/ pattern /搜索一行.另请参见下面更简单的一个:sed -n'/ abc /,/ efg/p'p用于打印,-n标志不显示所有行 (24认同)
  • @Lj.请你解释一下这个命令?我熟悉`sed`,但如果以前从未见过这样的表达. (11认同)
  • 这没有找到文件,它从单个文件返回匹配的部分 (3认同)
  • @Anthony,它记录在 sed 的手册页的地址下。重要的是要认识到 /abc/ 和 /efg/ 是一个地址。 (3认同)

att*_*tti 77

这是一个灵感来自这个答案的解决方案:

PARAMS:

  • -z将输入视为一组行,每行以零字节而不是换行符结束.即grep威胁输入作为一个大的线.

  • -l 打印通常从中输出的每个输入文件的名称.

  • (?s)激活PCRE_DOTALL,这意味着'.' 找到任何字符或换行符.

  • 这很棒.我只有一个问题.如果`-z`选项指定grep将换行处理为`零字节字符'那么为什么我们需要正则表达式中的`(?s)`?如果它已经是非换行符,那么```不能直接匹配它吗? (5认同)
  • -z在MacOS上绝对不可用 (3认同)

小智 32

sed应该足够像上面提到的海报LJ,

而不是!d你可以简单地使用p来打印:

sed -n '/abc/,/efg/p' file
Run Code Online (Sandbox Code Playgroud)


sag*_*age 13

我非常依赖pcregrep,但是对于更新的grep,你不需要为它的许多功能安装pcregrep.只是用grep -P.

在OP的问题的例子中,我认为以下选项很好地工作,第二个最佳匹配我如何理解这个问题:

grep -Pzo "abc(.|\n)*efg" /tmp/tes*
grep -Pzl "abc(.|\n)*efg" /tmp/tes*
Run Code Online (Sandbox Code Playgroud)

我将文本复制为/ tmp/test1并删除了'g'并保存为/ tmp/test2.这是输出显示第一个显示匹配的字符串,第二个显示只有文件名(典型的-o是显示匹配,典型的-l是仅显示文件名).请注意,'z'对于多行是必需的,'(.| \n)'表示匹配'换行符以外的任何内容'或'换行符' - 即任何东西:

user@host:~$ grep -Pzo "abc(.|\n)*efg" /tmp/tes*
/tmp/test1:abc blah
blah blah..
blah blah..
blah blah..
blah efg
user@host:~$ grep -Pzl "abc(.|\n)*efg" /tmp/tes*
/tmp/test1
Run Code Online (Sandbox Code Playgroud)

要确定您的版本是否足够新,请运行man grep并查看顶部附近是否显示类似的内容:

   -P, --perl-regexp
          Interpret  PATTERN  as a Perl regular expression (PCRE, see
          below).  This is highly experimental and grep -P may warn of
          unimplemented features.
Run Code Online (Sandbox Code Playgroud)

那是来自GNU grep 2.10.


g.r*_*ket 10

这可以通过首先使用tr用其他字符替换换行来轻松完成:

tr '\n' '\a' | grep -o 'abc.*def' | tr '\a' '\n'
Run Code Online (Sandbox Code Playgroud)

在这里,我使用警报字符\a(ASCII 7)代替换行符.这几乎从未在您的文本中找到,并且grep可以与a .匹配,或者与其特定匹配\a.

  • 这是我的方法,但我使用的是“\0”,因此需要“grep -a”并匹配“\x00”……你帮助我简化了!`回显 $log | tr '\n' '\0' | grep -aoE“错误:.*?\x00安装.*?失败\!” | tr '\0' '\n'` 现在是 `echo $log | tr '\n' '\a' | '\n' '\a' | grep -oE "错误: .*?\a安装 .*? 失败\!" | tr '\a' '\n'` (2认同)

sun*_*ica 6

如果你可以使用Perl,你可以很容易地做到这一点.

perl -ne 'if (/abc/) { $abc = 1; next }; print "Found in $ARGV\n" if ($abc && /efg/); }' yourfilename.txt
Run Code Online (Sandbox Code Playgroud)

您也可以使用单个正则表达式执行此操作,但这涉及将文件的全部内容转换为单个字符串,这可能最终会占用大文件的太多内存.为了完整性,这是方法:

perl -e '@lines = <>; $content = join("", @lines); print "Found in $ARGV\n" if ($content =~ /abc.*efg/s);' yourfilename.txt
Run Code Online (Sandbox Code Playgroud)


小智 6

awk一线:

awk '/abc/,/efg/' [file-with-content]
Run Code Online (Sandbox Code Playgroud)

  • 如果文件中不存在结束模式,或者缺少最后一个结束模式,则将从abc到文件结尾愉快地打印。您可以解决该问题,但是它将使脚本非常复杂。 (4认同)

fra*_*nkc 5

我不知道用grep怎么做,但是我会用awk做这样的事情:

awk '/abc/{ln1=NR} /efg/{ln2=NR} END{if(ln1 && ln2 && ln1 < ln2){print "found"}else{print "not found"}}' foo
Run Code Online (Sandbox Code Playgroud)

但是,你需要注意如何做到这一点.你想要正则表达式匹配子字符串或整个单词吗?根据需要添加\ w标签.此外,虽然这严格符合您所说的示例,但在ecg之后第二次出现abc时,它并不能正常工作.如果你想处理它,在/ abc/case等中添加一个if.


小智 5

如果您愿意使用上下文,可以通过键入来实现

grep -A 500 abc test.txt | grep -B 500 efg
Run Code Online (Sandbox Code Playgroud)

这将显示“abc”和“efg”之间的所有内容,只要它们彼此相距在 500 行以内。