我编写的脚本会执行某些操作,最后将一些行添加到其自己的日志文件中。我只想保留日志文件的最后n行(例如1000行)。可以通过以下方式在脚本末尾完成此操作:
tail -n 1000 myscript.log > myscript.log.tmp
mv -f myscript.log.tmp myscript.log
但是还有更干净,更优雅的解决方案吗?也许通过一个命令完成?
我编写的脚本会执行某些操作,最后将一些行添加到其自己的日志文件中。我只想保留日志文件的最后n行(例如1000行)。可以通过以下方式在脚本末尾完成此操作:
tail -n 1000 myscript.log > myscript.log.tmp
mv -f myscript.log.tmp myscript.log
但是还有更干净,更优雅的解决方案吗?也许通过一个命令完成?
Answers:
可能是这样,但正如其他人所说,最安全的选择是生成新文件,然后移动该文件以覆盖原始文件。
下面的方法将这些行加载到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
该实用程序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
在写入输出文件之前先吸收其所有输入。这允许构造读取和写入同一文件的管道。
sponge
。对于所有学习过艰苦方法的人都非常有用sort importantfile.txt > importantfile.txt
:)
作为记录,ed
您可以做类似的事情
ed -s infile <<\IN
0r !tail -n 1000 infile
+1,$d
,p
q
IN
这将打开infile
并r
进入的输出tail -n 1000 infile
(即,将输出插入第一行之前),然后从最初的第一行删除到文件末尾。替换,p
为w
以就地编辑文件。
请记住,尽管该ed
解决方案不适用于大文件。
您可以在脚本中执行的是实现日志轮换的逻辑。通过函数进行所有日志记录:
log()
{
...
}
首先,此功能执行以下操作:
printf "%s\n" "$*" >> logfile
然后,它检查文件的大小或以某种方式确定文件是否需要旋转。在这一点上,该文件logfile.1
,如果存在的话,被删除的文件logfile.0
,如果存在,重命名为logfile.1
和logfile
被重命名为logfile.0
。
决定是否旋转可以基于脚本本身中维护的计数器。当达到1000时,它将重置为零。
如果始终要求严格修剪到1000行,则脚本可以在启动时计算日志文件中的行数,并相应地初始化计数器(或者如果计数已经达到或超过1000,则立即进行轮换)。
或者,您可以获取尺寸(例如使用)wc -c logfile
,并根据超过特定尺寸进行旋转。这样,就不必扫描文件来确定条件。
我确实使用了代替mv
该cp
命令的命令来实现这一点,即您可以在运行软件的地方拥有一些日志文件。也许在不同的用户主目录或应用程序目录中,并且确实将所有日志作为硬链接存储在一个位置。如果使用该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"
相等文件,因为它是硬链接。
logrotate
是一个优雅的解决方案