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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用grep查找多行的模式?相关的知识,希望对你有一定的参考价值。

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

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

应该匹配。

答案

Grep不足以进行此操作。

在大多数现代Linux系统中都可以找到pcregrep

pcregrep -M  'abc.*(
|.)*efg' test.txt

其中-M, - multiline允许模式匹配多行

还有一个较新的pcre2grep。两者都是由PCRE project提供的。

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

% sudo port install pcre2 

并通过Homebrew作为:

% brew install pcre

或者是pcre2

% brew install pcre2
另一答案

可悲的是,你做不到。来自grep文档:

对于包含与给定PATTERN匹配的行,grep搜索命名输入FILE(或标准输入,如果没有文件被命名,或者如果单个连字符 - ( - )作为文件名)。

另一答案

我几天前发布了一个grep替代方案,它可以通过多行匹配或使用条件直接支持它 - 希望它对于在这里搜索的人有用。这是示例的命令的样子:

多线:sift -lm 'abc.*efg' testfile 条件:sift -l 'abc' testfile --followed-by 'efg'

您还可以指定'efg'必须在一定数量的行内跟随'abc': sift -l 'abc' testfile --followed-within 5:'efg'

你可以在sift-tool.org找到更多信息。

另一答案

虽然sed选项是最简单和最简单的,但是LJ的单行可能不是最便携的。那些坚持使用C Shell版本的人需要逃避他们的刘海:

sed -e '/abc/,/efg/!d' [file]

遗憾的是,这不适用于bash等人。

另一答案

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

grep -A 500 abc test.txt | grep -B 500 efg

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

另一答案

如果你需要两个单词彼此靠近,例如不超过3行,你可以这样做:

find . -exec grep -Hn -C 3 "abc" {} ; | grep -C 3 "efg"

相同的示例,但仅过滤* .txt文件:

find . -name *.txt -exec grep -Hn -C 3 "abc" {} ; | grep -C 3 "efg"

如果你想用正则表达式找到,你也可以用grep命令替换egrep命令。

另一答案
#!/bin/bash
shopt -s nullglob
for file in *
do
 r=$(awk '/abc/{f=1}/efg/{g=1;exit}END{print g&&f ?1:0}' file)
 if [ "$r" -eq 1 ];then
   echo "Found pattern in $file"
 else
   echo "not found"
 fi
done
另一答案

你可以使用grep,因为你不喜欢模式的顺序。

grep -l "pattern1" filepattern*.* | xargs grep "pattern2"

grep -l "vector" *.cpp | xargs grep "map"

grep -l将找到与第一个模式匹配的所有文件,而xargs将为第二个模式grep。希望这可以帮助。

另一答案

使用silver searcher

ag 'abc.*(
|.)*efg'

类似于持票人的回答,但用ag代替。银色搜索者的速度优势可能会在这里闪耀。

另一答案

我使用它来使用grep的-P选项从多个fasta文件中提取fasta序列:

grep -Pzo ">tig00000034[^>]+" file.fasta > desired_sequence.fasta

-P用于基于perl的搜索-z使得行结束为0字节而不是换行符char -o以捕获匹配的内容,因为grep返回整行(在这种情况下,因为你做了-z是整个文件)。正则表达式的核心是[^>],转换为“不大于符号”

另一答案

作为Balu Mohan的答案的替代方案,可以仅使用grepheadtail来强制执行模式的顺序:

for f in FILEGLOB; do tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep "pattern2" &>/dev/null && echo $f; done

不过,这个不是很漂亮。格式化更可读:

for f in FILEGLOB; do
    tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null 
    | grep -q "pattern2" 
    && echo $f
done

这将打印"pattern2"之后"pattern1"出现的所有文件的名称,或两者出现在同一行上的名称:

$ echo "abc
def" > a.txt
$ echo "def
abc" > b.txt
$ echo "abcdef" > c.txt; echo "defabc" > d.txt
$ for f in *.txt; do tail $f -n +$(grep -n "abc" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep -q "def" && echo $f; done
a.txt
c.txt
d.txt

说明

  • tail -n +i - 在ith之后打印所有行,包括在内
  • grep -n - 在匹配的行前加上行号
  • head -n1 - 仅打印第一行
  • cut -d : -f 1 - 使用:作为分隔符打印第一个切割列
  • 2>/dev/null - 如果tail表达式返回空,则会出现$()错误输出
  • grep -q - 沉默grep并在找到匹配时立即返回,因为我们只对退出代码感兴趣
另一答案

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

sed -e '/abc/,/efg/!d' [file-with-content]
另一答案

这也应该有用吗?!

perl -lpne 'print $ARGV if /abc.*?efg/s' file_list

$ARGV包含从换行符中的file_list /s修饰符搜索时当前文件的名称。

另一答案

filepattern *.sh对于防止检查目录很重要。当然,一些测试也可以防止这种情况。

for f in *.sh
do
  a=$( grep -n -m1 abc $f )
  test -n "${a}" && z=$( grep -n efg $f | tail -n 1) || continue 
  (( ((${z/:*/}-${a/:*/})) > 0 )) && echo $f
done

grep -n -m1 abc $f 

搜索最多1个匹配并返回(-n)亚麻布。如果找到匹配(测试-n ...),找到efg的最后一个匹配(找到所有并使用尾部-n 1取最后一个)。

z=$( grep -n efg $f | tail -n 1)

否则继续。

由于结果类似于18:foofile.sh String alf="abc";,我们需要从“:”切断直到行尾。

((${z/:*/}-${a/:*/}))

如果第二个表达式的最后一个匹配超过第一个表达式的第一个匹配,则应返回正结果。

然后我们报告文件名echo $f

另一答案

如果您对要搜索的2个字符串'abc'和'efg'之间的距离有所估计,您可以使用:

grep -r。 -e'abc'-A num1 -B num2 | grep'efg'

这样,第一个grep将返回行后面带有'abc'加#num1行的行,然后返回#num2行,第二个grep将筛选所有这些行以获得'efg'。然后你就会知道它们一起出现在哪些文件中。

另一答案

这应该工作:

cat FILE | egrep 'abc|efg'

如果有多个匹配,您可以使用grep -v过滤掉

另一答案

这是一个受this answer启发的解决方案:

  • 如果'abc'和'efg'可以在同一行: grep -zl 'abc.*efg' <your list of files>
  • 如果'abc'和'efg'必须在不同的行上: grep -Pzl '(?s)abc.* .*efg' <your list of files>

PARAMS:

  • -z将输入视为一组行,每行以零字节而不是换行符结束。即grep将输入威胁为一条大线。
  • -l打印通常打印输出的每个输入文件的名称。
  • (?s)激活PCRE_DOTALL,这意味着'。'找到任何字符或换行符。
另一答案

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

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

sed -n '/abc/,/efg/p' file
另一答案

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

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

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

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

user@host:~$ grep -Pzo "abc(.|
)*efg" /tmp/tes*
/tmp/test1:abc blah
blah blah..
blah blah..
blah blah..
blah efg
user@host:~$ grep -Pzl "abc(.|
)*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

以上是关于如何使用grep查找多行的模式?的主要内容,如果未能解决你的问题,请参考以下文章

使用 grep 和 sed 在 shell 中查找和替换同一文件中的多行

如何查找不包含给定字符串模式的文件?

linux 如何将一行注释掉

grep 命令系列:如何在 UNIX 中根据文件内容查找文件

如何使用grep从文件中循环模式[重复]

如何使用 preg_grep 查找 url