问答中心分类: REGEX如何使用grep跨多行查找模式?
0
匿名用户 提问 3月 前

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

blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..

应匹配。

26 Answers
0
LJ. 回答 3月 前

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

sed -e '/abc/,/efg/!d' [file-with-content]
shiggity 回复 3月 前

这不会找到文件,而是从单个文件返回匹配的部分

Anthony 回复 3月 前

@Lj。你能解释一下这个命令吗?我很熟悉sed,但如果我以前从未见过这样的表情。

Squidly 回复 3月 前

@Anthony,这记录在sed的手册页中,地址下面。重要的是要认识到/abc/&/efg/是一个地址。

Michael Scheper 回复 3月 前

我怀疑这个答案会有帮助,如果它有更多的解释,在这种情况下,我会再次投票。我知道一点sed,但不足以在半个小时的磨蹭之后用这个答案生成一个有意义的退出代码。提示:“RTFM”很少在StackOverflow上获得投票,正如你之前的评论所示。

woojoo666 回复 3月 前

我同意,最好有一个解释:)

phil_w 回复 3月 前

通过示例快速解释:sed“1,5d”:删除1到5之间的行。sed“1,5!d:删除不在1和5之间的行(即保持行在1和5之间),然后可以使用/pattern/搜索行,而不是数字。另请参阅下面更简单的一个:sed-n’/abc/,/efg/p’p用于打印,-n标志不显示所有行

TamaMcGlinn 回复 3月 前

这个答案不正确;因为您只使用第一行和最后一行,所以它比所问的问题匹配得多。

elig 回复 3月 前

用我自己的话来说:上面的命令告诉sed删除所有其他行,而不是那些可以找到的从开头保持“abc”到结尾包含“efg”的行的范围内的行!d表示反转匹配,因此d(elete)将删除其余的。/abc/,/efg/是由两个模式搜索组成的范围。

kyb 回复 3月 前

如何排除/efg/从输出?

Brad Parks 回复 3月 前

sed -e '/abc/,/efg/!d' | tail -n+2 | sed -e '$ d'

chb 回复 3月 前

我找到了本教程由Bruce Barnett编写有助于重新调整这个想法的用途,供我自己使用。

0
atti 回答 3月 前

以下是一个受启发的解决方案这个答案:

  • 如果“abc”和“efg”可以在同一行上:
    grep -zl 'abc.*efg'
  • 如果“abc”和“efg”必须在不同的行上:
    grep -Pzl '(?s)abc.*\n.*efg'

参数:

  • -P使用与perl兼容的正则表达式(PCRE)。
  • -z将输入视为一组行,每行以零字节而不是换行符终止。i、 e.grep将输入视为一大行。请注意,如果您不使用-l它将显示匹配项,后跟NUL字符,请参阅注释。
  • -l仅列出匹配的文件名。
  • (?s)激活PCRE_DOTALL,这意味着“.”查找任何字符或换行符。
Sparhawk 回复 3月 前

@syntaxerror不,我认为这只是一个小写lAFAIK没有号码-1选项

syntaxerror 回复 3月 前

看来你毕竟是对的,也许我在测试时打错了字。无论如何,对不起,我留下了一条错误的线索。

Durga Swaroop 回复 3月 前

这太棒了。关于这一点,我只有一个问题。如果-zoptions指定grep将换行视为zero byte characters那我们为什么需要(?s)在正则表达式中?如果它已经是非换行符,不应该.能够直接匹配吗?

Zeke Fast 回复 3月 前

-z(也称为空数据)和(?s)正是您需要将多行与标准grep匹配的内容。MacOS上的用户,请对系统上-z或-null数据选项的可用性发表评论!

Dylan Nicholson 回复 3月 前

-z在MacOS上绝对不可用

Kvothe 回复 3月 前

只是这需要这两种模式按照这个特定的顺序,对吗?(这可以通过使用-E和“abc.*efg | efg.*abc”来修复。)

JP Zhang 回复 3月 前

@DylanNicholson你可以在MacOS上安装GNU Grep

rogerdpack 回复 3月 前

如果您尝试查看比赛,请注意使用这种方法grep -zgrep -zo它附加了一个尾随空字符。你可以tr请参阅讨论stackoverflow.com/a/7167115/32453但还是很痛苦

rogerdpack 回复 3月 前

你可以在苹果操作系统中找到它brew install grep然后使用ggrep

0
user3897784user3897784 回答 3月 前

sed应满足上述海报LJ的要求,
而不是d您可以简单地使用p打印:

sed -n '/abc/,/efg/p' file
0
sage 回答 3月 前

我在很大程度上依赖于pcregrep,但对于更新的grep,您不需要为其许多功能安装pcregrep。只需使用grep -P.
在OP问题的例子中,我认为以下选项效果很好,第二个最佳选项与我理解问题的方式相匹配:

grep -Pzo "abc(.|\n)*efg" /tmp/tes*
grep -Pzl "abc(.|\n)*efg" /tmp/tes*

我将文本复制为/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

要确定您的版本是否足够新,请运行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.

这来自GNU grep 2.10。

0
Gavin S. Yancey 回答 3月 前

这可以通过首先使用tr要用其他字符替换换行符:

tr '\n' '\a' | grep -o 'abc.*def' | tr '\a' '\n'

这里,我用的是报警字符,\a(ASCII 7)代替换行符。grep可以将其与.,或将其与\a.

Charlie Gorichanaz 回复 3月 前

这是我的方法,但我正在使用\0因此需要grep -a并匹配\x00…你帮我简化了!echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'现在是echo $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'

kyb 回复 3月 前

使用grep -o.