我有一个大约30.000.000行(半径会计)的文件,我需要找到给定模式的最后一个匹配项。
命令:
tac accounting.log | grep $pattern
给出了我需要的东西,但是它太慢了,因为操作系统必须先读取整个文件,然后再发送到管道。
因此,我需要快速的东西,可以从最后一行读取文件到第一行。
我有一个大约30.000.000行(半径会计)的文件,我需要找到给定模式的最后一个匹配项。
命令:
tac accounting.log | grep $pattern
给出了我需要的东西,但是它太慢了,因为操作系统必须先读取整个文件,然后再发送到管道。
因此,我需要快速的东西,可以从最后一行读取文件到第一行。
Answers:
tac
仅在您还使用grep -m 1
(假设GNU grep
)grep
在第一个比赛之后停止时才有帮助:
tac accounting.log | grep -m 1 foo
来自man grep
:
-m NUM, --max-count=NUM
Stop reading a file after NUM matching lines.
在你的问题,这两个例子tac
和grep
需要处理整个文件,以便使用tac
是一种毫无意义的。
因此,除非您使用,否则grep -m
根本不要使用tac
,只需解析输出grep
以获取最后一个匹配项:
grep foo accounting.log | tail -n 1
另一种方法是使用Perl或任何其他脚本语言。例如(where $pattern=foo
):
perl -ne '$l=$_ if /foo/; END{print $l}' file
要么
awk '/foo/{k=$0}END{print k}' file
tac
,我的意思是除非您也使用它,否则它无济于事,-m
因为该文件仍需要两个程序完整读取。否则,您可以像我一样搜索所有发生的事件,并仅保留最后一个tail -n 1
。
grep -m
,它应该非常有效。
grep -m
时。OP没有使用,-m
因此grep和tac都在处理整个事情。
awk
线的含义?
之所以
tac file | grep foo | head -n 1
在第一场比赛中没有停止是因为缓冲。
通常,head -n 1
读取一行后退出。因此,grep
应在写第二行时获取SIGPIPE并退出。
但是发生的事情是,由于其输出没有到达终端,因此对其进行了grep
缓冲。也就是说,直到它积累了足够的内存(在我使用GNU grep的测试中为4096字节)之前,它才开始编写它。
这意味着grep
在写入8192字节数据之前不会退出,因此可能需要很多行。
使用GNU grep
,您可以使用--line-buffered
来告诉它只要找到行就立即写线,而不管是否去终端,可以使它更快退出。因此,grep
将在找到的第二行退出。
但是grep
无论如何,对于GNU ,您可以使用-m 1
@terdon所示的方法,因为它在第一个匹配项时退出,效果更好。
如果您grep
不是GNU grep
,则可以使用sed
或awk
代替。但是tac
,作为GNU命令,我怀疑您会找到一个系统,tac
其中where grep
不是GNU grep
。
tac file | sed "/$pattern/!d;q" # BRE
tac file | P=$pattern awk '$0 ~ ENVIRON["P"] {print; exit}' # ERE
某些系统必须tail -r
执行与GNU相同的tac
操作。
需要注意的是,定期(可搜索)的文件,tac
并且tail -r
是有效的,因为他们落后读取这些文件,他们不只是完全读取该文件在内存中落后打印之前(如@ SLM战略经济对话的方式或tac
在非正规的文件会) 。
在既不可用tac
也不tail -r
可用的系统上,唯一的选择是perl
使用诸如或使用的编程语言来实现手工向后读取:
grep -e "$pattern" file | tail -n1
要么:
sed "/$pattern/h;$!d;g" file
但是那些意味着找到所有匹配项,并且只打印最后一个。
显示使用@Terdon好的答案的一些替代方法sed
:
$ sed '1!G;h;$!d' file | grep -m 1 $pattern
$ sed -n '1!G;h;$p' file | grep -m 1 $pattern
$ seq 10 > file
$ sed '1!G;h;$!d' file | grep -m 1 5
5
$ sed -n '1!G;h;$p' file | grep -m 1 5
5
作为奖励,这里的Perl标记更容易记住:
$ perl -e 'print reverse <>' file | grep -m 1 $pattern
$ perl -e 'print reverse <>' file | grep -m 1 5
5
sed
)可能比grep 5 | tail -n1
或慢几个数量级sed '/5/h;$!d;g'
。它还可能会使用大量内存。由于您仍在使用GNU的,因此可移植性并不高grep -m
。