我想过这个问题是否适合SE,希望你同意。
不久前,我在 SE 上询问如何在文件中查找文本,并仅保留包含我正在搜索的文本的匹配行。问题在这里: 如何使用 OS X 上的终端在文件中查找文本并仅保留相应的匹配行?
虽然答案完美无缺,但我现在想知道,为什么sed
这么快?在我的用例中,我有相当多的文件,总大小约为 30 GB。该sed
命令运行了大约 12 秒,这让我难以置信(使用普通 HDD)。在 12 秒内,该命令读取了 30 GB 的文本,截断每个文件以仅保留我正在过滤的相应行。这是如何运作的?(或者:这是什么魔法?)
实际的命令是:
find . -type f -exec sed -i'' '/\B\/foobar\b/!d' {} \;
答案1
可能的答案是:
- 30Gb 文件没有碎片(或碎片很少):所有硬盘驱动器在顺序访问(包括 SSD)方面都表现得更好,因为它们能够缓存大块文件。这使他们能够达到最佳表现。顺序访问将有助于各级缓存。
sed
是一个流编辑器;它一次只处理一行。这意味着它的内存占用很小。与emacs
或等文本编辑器不同vim
,它不需要在内存中维护文件的整个副本。- 您正在就地编辑文件(使用
-i
)(如 @Ramesh 所示,并在维基百科页面)创建一个临时文件,然后该文件成为旧文件。
所有这些意味着sed
能够执行几乎最少的文件操作:原始文件的每一行都被读取一次,并且只写入匹配的行。
您对正则表达式的选择也会影响性能,有时会以非常糟糕的方式影响性能:编码恐怖博客。
答案2
一个很好的例子是sed
使用临时文件来实际保存内容,然后替换原始文件。例如,您可以进行简单的测试来找到这一点。
cat test
This is a test file.
现在,运行ls -li
来检查索引节点号。
ls -li test
2368770 -rw-r--r-- 1 root root 22 Sep 12 08:46 test
现在,发出以下sed
命令来添加空行。
sed -i 's/2/B/' test
更改文件后,ls
再次发出命令并检查 inode 编号。
ls -li test
2368753 -rw-r--r-- 1 root root 22 Sep 12 08:48 test
我们可以看到inode号实际上已经改变了。因此,不是复制到同一个文件,而是sed
创建一个新的临时文件并将内容复制到新的临时文件,然后删除原始文件并与原始文件同步重命名 tmp 文件,这就是文件操作确实更快的原因之一。
引用自维基百科页面,
sed 是一个面向行的文本处理实用程序:它从输入流或文件中逐行读取文本到称为模式空间的内部缓冲区中。每读取一行就开始一个循环。对于模式空间,sed 应用通过 sed 脚本指定的一个或多个操作。 sed 实现了一种具有大约 25 个命令的编程语言,这些命令指定对文本的操作。对于每一行,运行脚本后 sed 通常会输出模式空间(由脚本修改的输入行)并从下一行再次开始循环。
要了解有关 的模式空间和保持空间概念的更多信息sed
,您应该阅读答案这里。
当 sed 逐行读取文件时,当前已读取的行将插入到模式缓冲区(模式空间)中。模式缓冲区就像临时缓冲区,即存储当前信息的暂存器。当您告诉 sed 打印时,它会打印模式缓冲区。
保留缓冲区/保留空间就像一个长期存储,这样您可以捕获某些内容,存储它并在 sed 处理另一行时重用它。您不直接处理保留空间,相反,如果您想对其执行某些操作,则需要将其复制或附加到模式空间。