优化GNU grep


8

我正在使用grep -E带有PATTERN文件的egrep()。(-f path/to/file)。

这是在文本流的无限循环中完成的。这意味着我无法立即累积所有输入并将其传递给grep(如*.log)。

有没有一种方法可以使grep从PATTERN文件中“保存”正在构建的NFA,以便下次运行?

我已经搜索过Google并没有运气地阅读文档。

我会尝试多解释一下。我需要使用正则表达式找到固定数量的字符串(这不是问题的一部分,但可以提出其他建议),例如IP地址,域等。搜索是在Internet的feed上完成的。您可以将其视为文本流。grep由于它是流,因此无法使用所有输入。我可以累积大量流并grep在其上使用(因此不在grep每行上使用),但这也很有限(比如说30秒钟)。

我知道grep正在根据其所有模式(从文件中)构建NFA。因此,我的问题是:grep由于它不会改变,我可以告诉它保存该NFA以便下次运行吗?这样可以节省我每次构建NFA的时间。


您是什么意思?这是在文本流中无限循环地完成的?您是说grep每行文本要运行一个吗?文字是从哪里来的?将tail -f是一个选择?
斯特凡Chazelas

假设我将流累积30秒钟,然后grep在该块上运行。
bergerg

1
仍然不清楚为什么需要运行grep几次。可能相关:为什么将1250个字符串与90k模式匹配如此之慢?
斯特凡Chazelas

5
grep用于处理文本流,但我仍然不明白为什么您需要运行多个实例。为什么不能将所有这些都提供给同一grep实例?为什么在喂食之前需要积累它们grep
斯特凡Chazelas

2
看一下flex,然后编写自己的程序,结果可能会更快。
user2064000

Answers:


14

不,没有这样的东西。通常,启动grep(分叉新进程,加载可执行文件,共享库,动态链接...)的成本要比编译正则表达式大得多,因此这种优化几乎没有意义。

虽然可以看到为什么将1250个字符串与90k模式匹配如此缓慢?有关某些GNU版本中的错误的信息,该错误grep会使大量正则表达式的速度特别慢。

可能在这里,您可以grep通过将大块提供给同一grep实例来避免多次运行,例如,将其用作协同进程并使用标记来检测结束。与zsh和GNU grep以及awkmawk以下之外的实现:

coproc grep -E -f patterns -e '^@@MARKER@@$' --line-buffered
process_chunk() {
  { cat; echo @@MARKER@@; } >&p & awk '$0 == "@@MARKER@@"{exit};1' <&p
}
process_chunk < chunk1 > chunk1.grepped
process_chunk < chunk2 > chunk2.grepped

尽管使用awkperl代替进行整个操作可能更简单。

但是,如果不需要将grep输出放入不同块的不同文件中,则可以始终这样做:

{
  cat chunk1
  while wget -qO- ...; done # or whatever you use to fetch those chunks
  ...
} | grep -Ef patterns > output

我有grep的veraion 3+,所以这不是问题。甚至没有考虑分叉开销。我想我将尝试按grep原样流式传输所有内容。谢谢。
bergerg

进程终止后,可执行文件和共享库是否不会保留在RAM缓冲区中(除非OP实际上在RAM上很低)?
德米特里·格里戈列耶夫

2
@DmitryGrigoryev,是的,很可能仍然需要在进程地址空间中进行映射并进行链接编辑。还有更多类似加载和解析语言环境数据,解析选项,环境...重点是regcomp()的成本在所有这些开销中得到了稀释。优化时要做的第一件事是首先避免运行多次抓图。
斯特凡Chazelas

1

因为它是流,所以不能在所有输入上使用grep。我可以累积大量流并在其上使用grep ...

您知道管道阻塞了吗?如果您通过管道将某些东西传送到grep而所有输入均不可用,则grep将等待直到可用为止,然后像输入一样一直继续。

$ ( echo a1; echo b1; sleep 5; echo a2 ) | grep 'a.'
a1
a2

编辑:例如,管道的工作方式cmd1 | cmd2是两个程序将同时启动,并且它们之间有65,536字节的“块缓冲区”。当cmd2尝试读取并且该缓冲区为空时,它将等待一个块可用。当cmd1尝试写入并且该缓冲区已满时,它将等待直到cmd2读取它。

据我了解,没有必要将输入切成块并将它们分别传递给grep。这已经自动完成了。

EDIT2:grep也应在流中找到结果后立即打印结果。在获得结果之前,无需完成流。


0

也许您可以“对所有输入使用grep”?使用nc(netcat),还是通过script,或通过其他类似的工具?尤其是如果您的patternfile具有可管理的大小(例如小于1000个正则表达式)。

第一个示例:您可以进行egrep一些流媒体连接:(此处显示的示例nc,但其他示例也可以适用)

prompt:/some/path $ nc somehost someport | egrep -f patternfile | gzip -c - > results.gz

# and while this is running, you can have a look at the growing results.gz:
prompt:/some/otherpath $ tail -f /some/path/results.gz | gzip -c - | less

(注意:您甚至可以touch /some/path/results.gz在启动nc命令之前进行操作:并且tail -f在该(空)文件上不要丢失任何内容。无论如何,results.gz将包含您要捕获的所有内容)

第二个例子:您甚至可以egrep在当前正在运行的shell会话中(并显示另一种跟踪进度的方式):

#in 1 terminal:
prompt:/home/userA $ script
Script command is started. The file is typescript.
prompt:/home/userA $ 
 ... doing here whatever you want (start IRC? etc) ...
prompt:/home/userA $ ctrl-d # to end the current script session
Script command is complete. The file is typescript.

#and in another terminal, while you are "doing here whatever you want" :
prompt:/home/somewhere $ tail -f /home/userA/typescript | egrep -f patternfile  | tee /some/place/to/store/results.gz

egrepgrep大多数系统上的的高效版本(请参阅以下有趣的信息:https ://swtch.com/~rsc/regexp/regexp1.html )


你甚至可以用exemple1这样的事情作为DD输出等
奥利维尔·杜拉克

有趣的是:正则表达式的已知部分越大,grep的效率就越高(例如:查找字符串或regexp s太多,比匹配慢得多,并且比匹配something慢得多something even much longer(后者允许regexp匹配跳过更大的部分)在大文件上,它基本上是通过“长度”比率“解析”时间来解析它的(例如,抓取1个已知字符比匹配40个已知字符的字符串慢40倍。我没有t pro prof it,但这确实很明显。)
Olivier Dulac
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.