我们有一个日志文件,每天都会将记录附加到同一文件中,我们不确定特定日期附加的记录数。我需要打印最后附加到文件的记录。
cat file.txt, (yesterday file)
Shyam
Raghu
cat file.txt, (today file)
Shyam
Raghu
Ravi
我们可以看到Ravi是文件中最近更新的记录,我只需要打印Ravi。
我尝试过使用tail -f
命令,但我也得到了前一天的记录,因为更新的记录是动态的,是否有任何脚本或命令只给我今天更新的记录?
答案1
您可以使用这个 41 行 TXR Lisp 程序作为守护进程来监视日志文件并实时生成带有时间戳的版本。
TXR 具有非常低的依赖性和较小的内存占用,但功能却很多。
首先是演示:
foo
我们从一个和bar
不存在的状态开始。foo
将是没有标记的日志文件。bar
将是带有标记的日志文件:
$ rm -f foo bar
我们stamp.tl
在后台运行该程序。我没有#!
在其中添加(哈希爆炸)行(供读者练习),因此我们使用txr
它。这-d
意味着它将作为守护进程将自己置于后台:
$ txr stamp.tl -d foo bar
好的,让我们开始制作内容foo
:
$ echo "first post" >> foo
$ cat foo
first post
发生了什么bar
?
$ cat bar
2021-08-20 06:40:06 first post
日志行显示在那里并带有时间戳。继续前进:
$ echo "second post" >> foo
$ cat bar
2021-08-20 06:40:06 first post
2021-08-20 06:40:17 second post
$ echo "third post" >> foo
$ cat bar
2021-08-20 06:40:06 first post
2021-08-20 06:40:17 second post
2021-08-20 06:40:24 third post
现在,让我们尝试一下。假设记录日志的软件foo
轮换日志。foo
消失然后从零长度重新开始:
$ rm foo
$ echo "rotated" >> foo
$ cat foo
rotated
会发生什么bar
?
$ cat bar
2021-08-20 06:40:06 first post
2021-08-20 06:40:17 second post
2021-08-20 06:40:24 third post
2021-08-20 06:40:49 rotated
rotated
正如您所看到的,它很好地捕获了这条线。
当然,真正的解决方案是修复没有时间戳的日志记录的原始软件,但这同时可能只是创可贴解决方案。
代码如下。该程序有三个选项。除了我们使用的上述内容之外-d
,它还有一个覆盖目标文件的选项(默认行为是追加)和一个使用完全缓冲进行写入的选项(默认是使用行缓冲)。
请注意,该程序不支持轮换输出日志,因为它只是打开文件并保持打开状态。为了支持外部日志轮转,程序必须在每次写入时打开和关闭文件。否则,实现内部旋转:在这么多行之后,关闭文件,进行旋转重命名,然后重新打开它。
;; Copyright 2021
;; Kaz Kylheku <[email protected]>
;; Vancouver, Canada
;; All rights reserved.
;;
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions are met:
;;
;; 1. Redistributions of source code must retain the above copyright notice,
;; this list of conditions and the following disclaimer.
;;
;; 2. Redistributions in binary form must reproduce the above copyright notice,
;; this list of conditions and the following disclaimer in the documentation
;; and/or other materials provided with the distribution.
;;
;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
;; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
;; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
;; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
;; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
;; POSSIBILITY OF SUCH DAMAGE.
(defun stamp (infile outfile opts)
(when (and opts.daemonize
(not (daemon t nil))) ;; don't chdir
(put-line "failed to daemonize")
(exit nil))
(let* ((line-buf (if opts.fully-buffered "" "l"))
(write-mode (if opts.overwrite `w@{line-buf}` `a@{line-buf}`)))
(with-resources ((out (open-file outfile write-mode) (close-stream out))
(in (open-tail infile) (close-stream in)))
(whilet ((line (get-line in)))
(let ((stamp (time-string-local (time) "%Y-%m-%d %H:%M:%S")))
(put-line `@stamp @line` out))))))
(define-option-struct prog-opts nil
(w overwrite :bool
"Overwrite the output file instead of appending.")
(d daemonize :bool
"Run in the background as a daemon")
(f fully-buffered :bool
"Writes to the output file are flushed whenever\ \
an I/O buffer fills up. The default behavior is to\ \
flush after every line.")
(nil help :bool
"List this help text."))
(defvarl prog-name *load-path*)
(defun usage ()
(put-line "\nUsage:\n")
(put-line ` @{prog-name} [ options ] <infile> [ <outfile> ]`))
(let ((o (new prog-opts)))
o.(getopts *args*)
(when o.help
(usage)
o.(opthelp)
(exit nil))
(match-case o.out-args
((@infile @outfile) (stamp infile outfile o))
((@infile) (stamp infile `@infile.stamped` o))
(@else (usage) (exit nil))))