tij*_*uco 56 grep tail text-processing files
我有一个大约有 30.000.000 行(Radius Accounting)的文件,我需要找到给定模式的最后一个匹配项。
命令:
tac accounting.log | grep $pattern
Run Code Online (Sandbox Code Playgroud)
给出了我需要的东西,但它太慢了,因为操作系统必须首先读取整个文件,然后发送到管道。
所以,我需要一些可以从最后一行到第一行读取文件的快速方法。
ter*_*don 60
tac仅当您还使用grep -m 1(假设 GNU grep)grep在第一场比赛后停止时才有帮助:
tac accounting.log | grep -m 1 foo
Run Code Online (Sandbox Code Playgroud)
来自man grep:
-m NUM, --max-count=NUM
Stop reading a file after NUM matching lines.
Run Code Online (Sandbox Code Playgroud)
在你的问题,这两个例子tac和grep需要处理整个文件,以便使用tac是一种毫无意义的。
因此,除非您使用 ,否则grep -m根本不要使用tac,只需解析 的输出grep即可获得最后一个匹配项:
grep foo accounting.log | tail -n 1
Run Code Online (Sandbox Code Playgroud)
另一种方法是使用 Perl 或任何其他脚本语言。例如(其中$pattern=foo):
perl -ne '$l=$_ if /foo/; END{print $l}' file
Run Code Online (Sandbox Code Playgroud)
或者
awk '/foo/{k=$0}END{print k}' file
Run Code Online (Sandbox Code Playgroud)
Sté*_*las 14
之所以
tac file | grep foo | head -n 1
Run Code Online (Sandbox Code Playgroud)
不会在第一场比赛中停止是因为缓冲。
通常,head -n 1读取一行后退出。所以grep应该得到一个 SIGPIPE 并在它写入第二行后立即退出。
但是发生的情况是,因为它的输出不会到达终端,所以会grep缓冲它。也就是说,直到它积累了足够多(在我使用 GNU grep 的测试中为 4096 字节)之前,它才会写入它。
这意味着grep它在写入 8192 字节的数据之前不会退出,所以可能有很多行。
使用 GNU grep,您可以通过使用--line-bufferedwhich 告诉它在找到行后立即写入行来使其更快退出,而不管是否进入终端。所以grep会在它找到的第二行退出。
但是grep无论如何,对于 GNU ,您可以使用-m 1@terdon 所示的代替,因为它在第一场比赛中退出会更好。
如果您grep不是 GNU grep,那么您可以使用sed或awk代替。但是tac 作为一个 GNU 命令,我怀疑你会找到一个系统tacwhere grepis not GNU grep。
tac file | sed "/$pattern/!d;q" # BRE
tac file | P=$pattern awk '$0 ~ ENVIRON["P"] {print; exit}' # ERE
Run Code Online (Sandbox Code Playgroud)
有些系统必须tail -r做与 GNU 相同的事情tac。
请注意,对于常规(可查找)文件,tac并且tail -r因为它们确实向后读取文件而高效,它们不仅仅是在向后打印之前完全读取内存中的文件(如@slm 的 sed 方法或tac在非常规文件上) .
在既不可用tac也不tail -r可用的系统上,唯一的选择是使用以下编程语言手动实现向后阅读perl:
grep -e "$pattern" file | tail -n1
Run Code Online (Sandbox Code Playgroud)
或者:
sed "/$pattern/h;$!d;g" file
Run Code Online (Sandbox Code Playgroud)
但这意味着找到所有匹配项并只打印最后一个。