如何连续跟踪正在删除和重新创建的日志文件?

如何连续跟踪正在删除和重新创建的日志文件?

我需要从每次程序运行时删除并重新创建的日志文件中提取信息。在(再次)检测到该文件存在后,我想将tail其用于某个正则表达式。

正则表达式将匹配几次,但结果总是相同的,我想只打印一次,然后在重新创建文件时返回监视。

我研究了检测文件创建的方法。一种方法是通过inotifywait,但这需要安装一个单独的包。

也许更简单的方法是利用stderr当删除和创建被尾随的文件时尾部打印:

tail: '/path/to/debug.log' has become inaccessible: No such file or directory
tail: '/path/to/debug.log' has appeared;  following new file

所以我申请了这个解决方案正在运行:

debug_file="/path/to/debug.log"

while true; do
    # Monitor the log file until the 'new file' message appears
    ( tail -F $debug_file 2>&1 & ) | grep -q "has appeared;  following new file"
    
    # After the new file message appears, switch to monitoring for the regexp
    tail -F "$debug_file" | while read -r line; do
        id=$(echo "$line" | sed -n 's/.* etc \([0-9]\+\),.*/\1/p')
        if [ -n "$id" ]; then
            echo "ID: $id"
            break  # Exit the inner loop after the first match
        fi
    done
done

但我不喜欢这个解决方案启动两个不同的tail进程。有没有一种方法可以达到相同的结果,但只使用 1 个tail进程?

然后切换“模式”,首先查找文件创建,然后查找正则表达式,一旦找到,就返回“备用”模式,等待日志文件被删除并再次创建。

inotifywait 是一个更优雅的解决方案吗?理想情况下,我想要一个可以轻松移植到 Windows CMD 的解决方案。

答案1

在我看来,你不需要外循环。将尾部输出通过管道传输到 read 中可能允许您在一种状态机中进行操作,此时您处于两种状态。正常的尾部状态和新的文件状态,您等待下一个匹配来打印“ID:”行。

不过,我想您会想在阅读时尝试将 stderr 重定向到 stdout 。向您的示例添加了一些伪代码。

debug_file="/path/to/debug.log"

# After the new file message appears, switch to monitoring for the regexp
# state="new_file"
tail -F "$debug_file" 2>&1 | while read -r line; do
    # if state = "new_file"
        id=$(echo "$line" | sed -n 's/.* etc \([0-9]\+\),.*/\1/p')
        if [ -n "$id" ]; then
            echo "ID: $id"
            # state = "id_printed"
        fi
    # else
        # if line contains "has appeared;  following new file"
            # state = new_file
        # echo "$line"
    # fi
done

编辑。如果文件中的正则表达式可以有多个匹配项,并且您只想匹配第一个,则需要使用上述解决方案。否则,您可能会使用创建一个有点复杂的单行代码tail -F | grep | sed(也可能使用 awk 而不是 sed)。根据您的要求,您甚至可以使用 -o 选项,仅使用 tail 和 grep 来完成任务。

例如尝试:tail -F /path/to/debug.log | grep -E '.* etc ([0-9]+),.*',然后看看你想如何从那里转换它。

答案2

使用TXR口齿不清:

(open-tail "/path/to/debug.log")  ;; returns a stream that follows rotating log

您只需从该流中读取输入,例如使用以下内容:

(with-stream (s (open-tail "/path/to/debug.log"))
  (whilet ((ln (get-line s)))
    (if-match `@nil etc @id,@nil` ln
      (put-line `ID: @id`))))

答案3

您需要在游戏中连续尾随文件并且仅输出唯一的行是:

tail -F "$debug_file" 2>/dev/null | awk '!seen[$0]++'

如果您只想考虑 sed 命令中的 regexpsed -n 's/.* etc \([0-9]\+\),.*/\1/p'生成的部分行,您可以使用 GNU awk 的 3rg arg 来执行此操作(未经测试)match()

tail -F "$debug_file" 2>/dev/null |
awk 'match($0,/.* etc ([0-9]+),/,a) && !seen[a[1]]++ { print "ID: " a[1] }'

或者使用任何 awk:

tail -F "$debug_file" 2>/dev/null |
awk '/.* etc [0-9]+,/ && sub(/.* etc /,"") && sub(/,.*/,"") && !seen[$0]++ { print "ID: " $0 }'

如果您想丢弃在输入的上一次迭代中看到的任何“ID”,那么您可以这样做:

tail -F "$debug_file" 2>&1 |
awk '
    /^tail: \047.*\047 has appeared;  following new file$/ { delete seen }
    ... code from above ...
'

假设您要跟踪的文件本身不能包含与该^tail:.*正则表达式匹配的行。

这很有可能是这样的:

awk '/.* etc [0-9]+,/ && sub(/.* etc /,"") && sub(/,.*/,"") && !seen[$0]++ { print "ID: " $0 }'

实际上可以写成更简洁的形式,例如:

awk '$(X-1) == "etc" && !seen[$X]++ { print "ID: " $X+0 }'

包含 ID 号的空格分隔字段号在哪里X,但没有看到日志文件的示例,我不知道这是否有效,如果有效,X会有什么值,也不确切情况会是什么样子。

相关内容