给定一个 425M 大小的文本文件,内容如下:
--START--
Data=asdfasdf
Device=B
Lorem=Ipsum
--END--
--START--
Data=asdfasdf
Lorem=Ipsum
Device=A
--END--
--START--
Device=B
Data=asdfasdf
--END--
...
任务sed
是打印--START--
和之间的所有内容--END--
(其中Device=A
包括)。提供了两种解决方案这里和这里。两个命令之间存在巨大的执行时间差异。第二个命令相当快,但需要更多说明它是如何工作的?
$ sed -n '/--START--/{:a;N;/--END--/!ba; /Device=A/p}' file
$ sed 'H;/--START--/h;/--END--/!d;x;/Device=A/!d' file
第一个命令的说明:
怎么运行的:
/--START--/{...}
每次到达包含 的行时--START--
,就运行大括号内的命令{...}
。
:a;
定义标签“a”。
N;
读取下一行并将其添加到模式空间。
/--END--/!ba
除非模式空间现在包含--END--
,否则跳回 labela
。
/Device=A/p
如果我们到达这里,这意味着模式空间以 开始--START--
并以 结束--END--
。另外,如果模式空间包含Device=A
,则打印 (p
) 它。
第二条命令说明:
sed 'H #add line to hold space /--START--/h #put START into hold space (substitute holded in) /--END--/!d #clean pattern space (start next line) if not END x #put hold space into pattern space /Device=A/!d #clean pattern space if it have not "Device=A" ' file
答案1
要记住的一件事是正则表达式匹配是“昂贵的”......因此模式缓冲区中的内容越多,搜索速度就越慢。
在这种特殊情况下,sed
必须找到三个模式(让我们将它们编号为 1、2 和 3):范围 START (1)、范围 END (2) 和该范围内的 MATCH (3)(如果有)。
两种解决方案之间的主要区别在于用于存储范围内所有行的缓冲区,这反过来又决定如何检测范围结束。
第一个解决方案的工作原理是在每行上搜索 START (1),一旦找到它,它就会开始将行追加到模式空间,并且必须在每次迭代时检查范围的 END (2)(每次添加一个模式空间中数据的新行,它再次搜索整个缓冲区END 以便知道何时停止)。一旦找到它,它在整个模式空间中搜索 MATCH (3)。
第二个解决方案的工作方式不同:它通过 无条件地在保留空间中累积行H
,它对每行进行两次模式匹配:分别确定范围的 START (1) 和 END (2)。这是非常快。一旦检测到范围的结束,它就会x
更改缓冲区(因此现在模式空间包含在保留空间中累积的所有行)并且在整个模式空间中搜索 MATCH (3)。
如您所见,(3) 是完全相同的在这两种情况下:两个sed
脚本都运行一次 MATCH 搜索,一旦模式空间包含从 START 到 END 的所有行。因此,区分这两个解决方案的并不是 MATCH 的搜索。这里的主要区别是由(2)引起的:
第二个解决方案在每一行上搜索 END - 如果该行不包含 END,它将d
从模式空间中删除它并重新启动循环,即它会拉入另一行,然后再次,尝试找到END等等。在找到 END 之前,模式空间中永远不会多于一行。
相比之下,第一个解决方案将a;N;/--END--/!ba
在越来越大的文本缓冲区上一遍又一遍地执行,即使与先前运行的差异仅由一行组成。当处理大型文本文件时,这从来都不是一件好事 - 想象一下 START-END 范围跨越数千行......
简而言之:搜索范围的 END 会减慢速度。
与第二种技术相比,第一种技术有多慢的一个很好的例子可以在这里找到:
正如您在我的测试中看到的,第一个解决方案甚至无法完成测试。