从文件末尾到开头grep

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 grepgrep在第一场比赛后停止时才有帮助:

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)

在你的问题,这两个例子tacgrep需要处理整个文件,以便使用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)

  • 为什么说“tac [...] 需要处理整个文件”?tac 做的第一件事是寻找文件的末尾并从末尾读取一个块。您可以使用 strace(1) 自行验证这一点。当与 `grep -m` 结合使用时,它应该非常有效。 (6认同)
  • 我使用 tac 是因为我需要找到给定模式的最后一个匹配项。使用您的建议“grep -m1”,执行时间从 0m0.597s 变为 0m0.007s \o/。谢谢大家! (2认同)

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,那么您可以使用sedawk代替。但是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)

但这意味着找到所有匹配项并只打印最后一个。