如何在使用 tail、while、read 和的 shell 脚本中检测非滚动日志文件和模式匹配



我正在监视一个日志文件,如果PATTERNTHRESHOLD 秒内没有出现在其中,脚本应该打印"错误",否则,它应该打印"清除"。脚本工作正常,但前提是日志正在滚动。

我试过阅读"超时",但没有用。

log_file=/tmp/app.log
threshold=120
tail -Fn0 ${log_file} | 
while read line ; do
  echo "${line}" | awk '/PATTERN/ { system("touch pattern.tmp") }'

用于计算多久以前模式.tmp触摸和相同分配给DIFF

的代码
if [ ${diff} -gt ${threshold} ]; then
   echo "Error"
else
   echo "Clear"
done

仅当应用程序中打印了"任何"行时,它才会按预期工作.log。

如果应用程序因任何原因挂起并且日志停止滚动,则脚本不会有任何输出。

有没有办法检测tail的"无输出"并在那时执行一些命令?

看起来您遇到的问题是,当while阻塞输入时,循环内的时序计算永远不会有机会运行read。在这种情况下,您可以将tail输出通过管道传输到while true循环中,您可以在其中执行if read -t $timeout

log_file=/tmp/app.log
threshold=120
timeout=10
tail -Fn0 "$log_file" | while true; do
  if read -t $timeout line; then
    echo "${line}" | awk '/PATTERN/ { system("touch pattern.tmp") }'
  fi
  # code to calculate how long ago pattern.tmp touched and same is assigned to diff 
  if [ ${diff} -gt ${threshold} ]; then
    echo "Error"
  else
    echo "Clear"
  fi
done

正如 Ed Morton 指出的那样,在 bash 脚本中,所有大写变量名称都不是一个好主意,所以我使用了小写变量名称。

像这样简单的事情怎么样:

sleep "$threshold"
grep -q 'PATTERN' "$log_file" && { echo "Clear"; exit; }
echo "Error"

如果这不是您所需要的全部,请编辑您的问题以阐明您的要求。顺便说一句,不要对非导出的 shell 变量名称使用全部大写 - 谷歌一下。

为了进一步构建您的想法,在后台运行 awk 部分并连续循环进行检查可能是有益的。

#!/usr/bin/env bash
log_file="log.txt"
# threshold in seconds
threshold=10
# run the following process in the background
stdbuf -oL tail -f0n "$log_file" 
   | awk '/PATTERN/{system("touch "pattern.tmp") }' &
while true; do
    match=$(find . -type f  -iname "pattern.tmp" -newermt "-${threshold} seconds")
    if [[ -z "${match}" ]]; then
        echo "Error"
    else
        echo "Clear"
    fi
done
在我看来,

这就像一个看门狗计时器。我已经通过强制后台进程更新我的日志来实现这样的事情,所以我不必担心read -t.下面是一个工作示例:

#!/usr/bin/env bash
threshold=10
grain=2
errorstate=0
while sleep "$grain"; do
        date '+[%F %T] watchdog timer' >> log
done &
trap "kill -HUP $!" 0 HUP INT QUIT TRAP ABRT TERM
printf -v lastseen '%(%s)T'
tail -F log | while read line; do
        printf -v now '%(%s)T'
        if (( now - lastseen > threshold )); then
                echo "ERROR"
                errorstate=1
        else
                if (( errorstate )); then
                        echo "Recovered, yay"
                        errorstate=0
                fi
        fi
        if [[ $line =~ .*PATTERN.* ]]; then
                lastseen=$now
        fi
done

在一个窗口中运行它,等待$threshold秒以触发它,然后在另一个窗口中echo PATTERN >> log以查看恢复。

虽然这可以像您喜欢的那样精细(我在示例中将其设置为 2 秒(,但它确实会污染您的日志文件。

哦,请注意,printf '%(%s)T'格式需要 bash 版本 4 或更高版本。

最新更新