看来我在滥用grep
/ egrep
。
我试图在多行中搜索字符串,但找不到匹配项,但我知道我要查找的内容应该匹配。最初我以为我的正则表达式是错误的,但最终我读到这些工具是按行运行的(而且我的正则表达式太琐碎了,不可能成为问题)。
那么,将使用哪种工具来搜索多行模式?
grep
。国际海事组织,它们是紧密相关的,但不是虚假的。
"grep"
建议动词“ to grep”,并且最常见的答案(包括已接受的答案)不要使用grep。
看来我在滥用grep
/ egrep
。
我试图在多行中搜索字符串,但找不到匹配项,但我知道我要查找的内容应该匹配。最初我以为我的正则表达式是错误的,但最终我读到这些工具是按行运行的(而且我的正则表达式太琐碎了,不可能成为问题)。
那么,将使用哪种工具来搜索多行模式?
grep
。国际海事组织,它们是紧密相关的,但不是虚假的。
"grep"
建议动词“ to grep”,并且最常见的答案(包括已接受的答案)不要使用grep。
Answers:
这是sed
一个可以grep
在多行中为您提供类似行为的工具:
sed -n '/foo/{:start /bar/!{N;b start};/your_regex/p}' your_file
怎么运行的
-n
禁止打印每行的默认行为/foo/{}
指示它进行匹配foo
,并在弯曲线内进行匹配行的操作。替换foo
为图案的开始部分。:start
是一个分支标签,可帮助我们不断循环直到找到正则表达式的结尾。/bar/!{}
将对不匹配的行执行弯曲的内容bar
。替换bar
为图案的结尾部分。N
将下一行追加到活动缓冲区(sed
将此称为模式空间)b start
start
只要模式空间不包含,它将无条件地分支到我们先前创建的标签,以便继续追加下一行bar
。/your_regex/p
如果匹配则打印模式空间your_regex
。您应该用your_regex
要在多行中匹配的整个表达式替换。sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
sed: unterminated {
错误
这是使用Perl的一种更简单的方法:
perl -e '$f=join("",<>); print $& if $f=~/foo\nbar.*\n/m' file
或者(因为JosephR 接过sed
路线,我会无耻地窃取他的建议)
perl -n000e 'print $& while /^foo.*\nbar.*\n/mg' file
$f=join("",<>);
:这将读取整个文件,并将其内容(换行符和全部)保存到变量中$f
。然后foo\nbar.*\n
,我们尝试匹配,如果匹配则将其打印出来(特殊变量$&
保存找到的最后一个匹配项)。的///m
需要,使整个新行的正则表达式匹配。
该-0
设置输入记录分隔符。将其设置为00
激活“段落模式”,其中Perl将使用连续的换行符(\n\n
)作为记录分隔符。如果没有连续的换行符,则会一次读取(提取)整个文件。
难道不是大文件做到这一点,将整个文件加载到内存中,并且可能是一个问题。
一种方法是使用Perl。例如,这是一个名为的文件的内容foo
:
foo line 1
bar line 2
foo
foo
foo line 5
foo
bar line 6
现在,这是一些Perl,它将与以foo开头的任何行以及以bar开头的任何行匹配:
cat foo | perl -e 'while(<>){$all .= $_}
while($all =~ /^(foo[^\n]*\nbar[^\n]*\n)/m) {
print $1; $all =~ s/^(foo[^\n]*\nbar[^\n]*\n)//m;
}'
Perl,细分为:
while(<>){$all .= $_}
这会将整个标准输入加载到变量中 $all
while($all =~
虽然变量all
具有正则表达式.../^(foo[^\n]*\nbar[^\n]*\n)/m
正则表达式:foo在行的开头,后跟任意数量的非换行符,然后是换行符,紧接着是“ bar”,其余的行中都带有bar。/m
正则表达式末尾的意思是“跨多行匹配”print $1
打印正则表达式中带括号的部分(在本例中为整个正则表达式)s/^(foo[^\n]*\nbar[^\n]*\n)//m
删除正则表达式的第一个匹配项,因此我们可以匹配相关文件中多个正则表达式的情况并输出:
foo line 1
bar line 2
foo
bar line 6
perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo
grep替代筛选器支持多行匹配(免责声明:我是作者)。
假设testfile
包含:
<书> <title>催眠药</ title> <描述> Lorem ipsum dolor坐在amet,consectetur 忠实的精英,圣贤时代 Laboure et dolore magna aliqua </ description> </ book>
sift -m '<description>.*?</description>'
(显示包含描述的行)
结果:
测试文件:<描述> Lorem ipsum dolor坐在amet,安全 测试文件:adipiscing elit,sed do eiusmod tempor incididunt ut 测试文件:labour et dolore magna aliqua </ description>
sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename
(提取并重新格式化说明)
结果:
description =“ Lorem ipsum dolor sit amet,consectetur 忠实的精英,圣贤时代 劳动与dolore magna aliqua”
只需一个支持Perl-regexp
参数的普通grep即可P
完成此工作。
$ echo 'abc blah
blah blah
def blah
blah blah' | grep -oPz '(?s)abc.*?def'
abc blah
blah blah
def
(?s)
称为DOTALL修饰符,它使正则表达式中的点不仅与字符匹配,而且与换行符匹配。
-P
选项
我使用grep和-A选项以及另一个grep为我解决了这个问题。
grep first_line_word -A 1 testfile | grep second_line_word
-A 1选项在找到的行之后打印1行。当然,这取决于您的文件和单词组合。但是对我来说,这是最快,最可靠的解决方案。
假设我们有一个包含以下内容的文件test.txt:
blabla
blabla
foo
here
is the
text
to keep between the 2 patterns
bar
blabla
blabla
可以使用以下代码:
sed -n '/foo/,/bar/p' test.txt
对于以下输出:
foo
here
is the
text
to keep between the 2 patterns
bar
如果我们想获得两个模式之间的文本(不包括它们自己)。
假设我们有一个包含以下内容的文件test.txt:
blabla
blabla
foo
here
is the
text
to keep between the 2 patterns
bar
blabla
blabla
可以使用以下代码:
sed -n '/foo/{
n
b gotoloop
:loop
N
:gotoloop
/bar/!{
h
b loop
}
/bar/{
g
p
}
}' test.txt
对于以下输出:
here
is the
text
to keep between the 2 patterns
它是如何工作的,让我们逐步进行
/foo/{
当行包含“ foo”时被触发 n
用下一行替换模式空间,即单词“ here” b gotoloop
转到标签“ gotoloop” :gotoloop
定义标签“ gotoloop” /bar/!{
如果模式不包含“ bar” h
用图案替换容纳空间,因此“此处”保存在容纳空间中 b loop
分支到标签“ loop” :loop
定义标签“循环” N
将图案附加到容纳空间。:gotoloop
我们现在进入第4步,循环直到一行包含“ bar”/bar/
循环结束,找到“条”,它是模式空间 g
模式空间被包含在主循环中已保存的“ foo”和“ bar”之间的所有行的保留空间替换p
将模式空间复制到标准输出完成!