如何在日志文件中保留最后50行


21

我尝试在文件中保留最后50行,以便每分钟保存温度。我使用了以下命令:

tail -n 50 /home/pi/Documents/test > /home/pi/Documents/test

但是结果是空的测试文件。我认为,它将列出测试文件的最后50行并将其插入测试文件。当我使用此命令时:

tail -n 50 /home/pi/Documents/test > /home/pi/Documents/test2

它工作正常。test2文件中有50行。

谁能向我解释问题出在哪里?


2
诸如rrdtool之类的东西可能更适合于随时间保留N条记录(以及其他统计信息)。
thrig


2
经典的截断问题
haylem's

如果您使用python生成日志,则应查看logging模块
Wayne Werner

Answers:


29

问题在于您的外壳在运行命令之前正在建立命令管道。这与“输入和输出”无关,因为文件的内容在tail运行之前就已经消失了。它类似于:

  1. Shell打开>输出文件进行写入,将其截断
  2. Shell设置为将文件描述符1(用于stdout)用于该输出
  3. 外壳程序执行tail
  4. tail运行,打开/home/pi/Documents/test,那里什么也没找到

有多种解决方案,但关键是要了解问题,实际出了什么问题以及原因。

这将产生您想要的东西,

echo "$(tail -n 50 /home/pi/Documents/test)" > /home/pi/Documents/test

说明:

  • $() 称为执行命令替换 tail -n 50 /home/pi/Documents/test
  • 引号会保留输出中的换行符。
  • > /home/pi/Documents/test将输出重定向echo "$(tail -n 50 /home/pi/Documents/test)"到同一文件。

谢谢,一切正常!我还有一个问题。您能否逐步说明您的程序如何工作?
dorinand '16

1
但是,为什么在您的情况下bash不执行>首先?我不明白bash如何处理命令。有人可以解释吗?
dorinand '16

1
>在echo命令上,因此将在echo命令开始执行时执行。写入之前无法开始执行。变量替换是写命令的内容。它运行嵌套命令并通过替换该值来创建echo命令。
jobermark '16

当我尝试使用5000行而不是50行将相同的文件用于44gb日志文件时,出现错误bash: xrealloc: cannot allocate 18446744071562067968 bytes
Carmageddon

8

文件重定向首先清除文件的另一种解决方案是使用像这样spongemoreutilspackge:

tail -n 50 /home/pi/Documents/test | sponge /home/pi/Documents/test

6

这是因为bash首先处理重定向>,删除了文件的内容。然后执行命令。如果您使用>>,则最后50行将追加到文件中当前内容的末尾。在这种情况下,您将有相同的50行重复两次。

重定向到其他文件时,该命令将按预期工作。这是将文件的最后50行写入同名文件的一种方法:

tail -50 /home/pi/Documents/test > /home/pi/Documents/test2 && mv /home/pi/Documents/test2 /home/pi/Documents/test

这首先将最后50行写入一个临时文件,然后将其移动mv以替换原始文件。

如评论中所述,如果文件仍处于打开状态,则将无法使用。移动文件还会创建一个新的索引节点,并且可能会更改所有权和权限。使用临时文件执行此操作的更好方法是:

tail -50 /home/pi/Documents/test > /home/pi/Documents/test2 ; cat /home/pi/Documents/test2 > /home/pi/Documents/test

临时文件也可以删除,尽管每次发生时,其内容都会被覆盖。


谢谢。您能逐步解释一下bash执行什么操作吗?我无法想象它是如何工作的。
dorinand '16

tail -50 /home/pi/Documents/test >/tmp/foo && cat /tmp/foo >/home/pi/Documents/test
史蒂夫,2016年

1
请注意,如果日志文件在日志记录过程中仍处于打开状态(它将继续记录到原始的已删除文件),则此方法将无效。tempfile + move会导致一个新的索引节点(破坏任何硬链接),并可能具有不同的所有者或权限。 tail ... > temp ; cat temp > orig ; rm -f temp作品。
cas

4

既然您已经看到了Shell重定向的主要问题,下面是将文件修剪到最后50行的另一种方法:

file=/path/to/the/file
n=$(( $(wc -l < "$file") - 50 ))
[[ $n -gt 0 ]] && sed -i 1,${n}d "$file"

(GNU)使用-i“就地编辑”功能完成了这项艰苦的工作,该功能通过在临时文件中创建输出来在幕后起作用。其余各行为sed的运算设置数学,即:

  1. 计算文件(wc)中的行,然后减去50;分配给n
  2. 如果n 为正,请运行sed命令删除行1到n。

4
printf '%s\n' '1,$-50d'   w | ed -s /home/pi/Documents/tes

printf用于将命令(每行一个)传送到ed。这些ed命令是:

  • 1,$-50d -删除除最后50行外的所有行
  • w -将修改后的文件写回到磁盘

不涉及重定向,因此shell无法在读取输出文件之前覆盖它。

另外,与大多数形式的“就地”编辑(通常仅通过创建临时文件然后在原始文件上重命名来模拟“就地”编辑)不同,它ed实际上是在编辑原始文件-因此它会保留相同的inode(以及所有者,组和权限-tempfile + mv 始终会更改inode,并可能根据情况更改其他inode )。


4

在稍微不同的轨道上,您可以用来logrotate(8)定期将日志文件备份为增量命名的文件,然后删除旧文件。

这是管理主系统日志文件的方法,以防止它们增长太长时间。

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.