如何只保留日志文件的最后n行?


18

我编写的脚本会执行某些操作,最后将一些行添加到其自己的日志文件中。我只想保留日志文件的最后n行(例如1000行)。可以通过以下方式在脚本末尾完成此操作:

tail -n 1000 myscript.log > myscript.log.tmp
mv -f myscript.log.tmp myscript.log

但是还有更干净,更优雅的解决方案吗?也许通过一个命令完成?


logrotate是一个优雅的解决方案
Ipor Sircer

1
我已经想到了,但是logrotate配置会比脚本本身更长……
dr01 2016年

如果logrotate过大,那么您的解决方案将尽其所能。使用sed / awk,您也许可以在一行中完成此操作,但是内部却没有临时文件,因此它可能效率不高且可读性较低。
kba与莫妮卡站在

Answers:


28

可能是这样,但正如其他人所说,最安全的选择是生成新文件,然后移动该文件以覆盖原始文件。

下面的方法将这些行加载到BASH中,因此取决于from中的行数tail,这将影响本地外壳程序存储日志行内容的内存使用率。

下面的内容也删除了空行(如果它们存在于日志文件的末尾)(由于BASH评估的行为"$(tail -1000 test.log)"),因此在所有情况下都不能给出100%准确的截断,但是根据您的情况,这可能就足够了。

$ wc -l myscript.log
475494 myscript.log

$ echo "$(tail -1000 myscript.log)" > myscript.log

$ wc -l myscript.log
1000 myscript.log

聪明。我将此标记为可接受的答案,因为它不需要安装其他工具。希望我能接受您的回答和@ John1024的回答。
dr01年

你的来电。我提高了对海绵解决方案的支持,因为我对此一无所知,并且保证不会与空的日志行混淆。根据日志文件的内容,此解决方案有可能做到这一点。
parkamark

该解决方案具有竞争条件。如果您不走运,则将文件重定向到文件之前会发生从文件读取的操作,最后您将得到一个空文件。
Coroos

21

该实用程序sponge仅用于这种情况。如果已安装,则可以写成两行:

tail -n 1000 myscript.log | sponge myscript.log

通常,在写入文件的同时读取文件是不可靠的。 sponge通过myscript.log直到tail完成读取并终止管道之后才写入来解决此问题。

安装

sponge在类似Debian的系统上安装:

apt-get install moreutils

sponge在RHEL / CentOS系统上安装,请添加EPEL存储库,然后执行以下操作:

yum install moreutils

文献资料

来自man sponge

sponge读取标准输入并将其写到指定文件中。与Shell重定向不同,sponge在写入输出文件之前先吸收其所有输入。这允许构造读取和写入同一文件的管道。


2
+1谢谢,我不知道sponge。对于所有学习过艰苦方法的人都非常有用sort importantfile.txt > importantfile.txt:)
dr01

4

绝对“ tail + mv”要好得多!但是对于gnu sed我们可以尝试

sed -i -e :a -e '$q;N;101,$D;ba' log

3

作为记录,ed您可以做类似的事情

ed -s infile <<\IN
0r !tail -n 1000 infile
+1,$d
,p
q
IN

这将打开infiler进入的输出tail -n 1000 infile(即,将输出插入第一行之前),然后从最初的第一行删除到文件末尾。替换,pw以就地编辑文件。
请记住,尽管该ed解决方案不适用于大文件。


0

您可以在脚本中执行的是实现日志轮换的逻辑。通过函数进行所有日志记录:

log()
{
   ...
}

首先,此功能执行以下操作:

printf "%s\n" "$*" >> logfile

然后,它检查文件的大小或以某种方式确定文件是否需要旋转。在这一点上,该文件logfile.1,如果存在的话,被删除的文件logfile.0,如果存在,重命名为logfile.1logfile被重命名为logfile.0

决定是否旋转可以基于脚本本身中维护的计数器。当达到1000时,它将重置为零。

如果始终要求严格修剪到1000行,则脚本可以在启动时计算日志文件中的行数,并相应地初始化计数器(或者如果计数已经达到或超过1000,则立即进行轮换)。

或者,您可以获取尺寸(例如使用)wc -c logfile,并根据超过特定尺寸进行旋转。这样,就不必扫描文件来确定条件。


0

我确实使用了代替mvcp命令的命令来实现这一点,即您可以在运行软件的地方拥有一些日志文件。也许在不同的用户主目录或应用程序目录中,并且确实将所有日志作为硬链接存储在一个位置。如果使用该mv命令,则会丢失硬链接。如果改用cp命令,则将保留此硬链接。

我的代码是这样的:

TMP_FILE="$(mktemp "${TMPFILENAME}.XXX")"

for FILE in "${LOGFILE_DIR}"/* ; do
    tail -n $MAXLINES "${FILE}" > "${TMP_FILE}"
    if [ $(ls -g "${TMP_FILE}" | awk '{print $4}') -lt $(ls -g "${FILE}" | awk '{print $4}') ] ; then
        cp "${TMP_FILE}" "${FILE}"
    fi
done   

因此,如果文件位于同一文件系统上,则您可能会给用户一些不同的权限,并且${LOGFILE_DIR}像我一样修改长度。

如果是该mv命令,则会丢失文件之间的硬链接,因此第二个文件与第一个文件的连接不再紧密-可能放置在其他位置。

如果在另一个地方,您不允许某人删除文件,则您的日志将保持在一起,并可以通过您自己的脚本很好地控制。

logrotate也许更好。但是我对此解决方案感到满意。

不要被“”打扰,但在我的情况下,其中包含一些带有空格和其他特殊字母的文件,如果我不使用“”或{},那么整个文件将无法正常工作。

例如,有一个Dir,其中较旧的文件会自动压缩到中,OLDFILE.zip并且所有压缩后的文件也会在File中列出,.zip_log因此.zip_log该Dir也存在,但在LOGFILE_DIR我已经拥有的文件中:

ln .zip_log "${LOGFILE_DIR}/USER_ZIP_log"

相等文件,因为它是硬链接。

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.