【问题标题】:How to timeout a tail pipeline properly on shell如何在外壳上正确超时尾部管道
【发布时间】:2019-12-19 22:36:40
【问题描述】:

我正在实现monitor_log 函数,该函数将从运行日志中跟踪最新行并使用while循环检查所需的字符串,超时逻辑应该是当尾部日志运行超过300秒时,它必须关闭尾部和while循环管道。

我发现的大问题是对于某些服务器,运行日志NOT 不断生成,这意味着tail -n 1 -f "running.log" 也将NOTwhile 循环生成输出以供使用,因此超时检查逻辑if [[ $(($SECONDS - start_timer)) -gt 300 ]] 不会正确命中。

例如我将300 秒设置为超时,但如果running.log300 秒之前停止生成新行并且在30 分钟内没有新行,tail 将不会在30 分钟内生成新输出,因此while 循环中的超时检查逻辑不会在30 分钟内命中,因此即使在300 秒后它也会保持拖尾并且不会中断,如果没有来自@987654334 的新行@forever,超时检查逻辑不会永远命中。

function monitor_log() {
  if [[ -f "running.log" ]]; then
    # Timer start
    start_timer=$SECONDS
    # Tail the running log last line and keep check required string
    tail -n 1 -f "running.log" | while read tail_line
    do
      if [[ $(($SECONDS - start_timer)) -gt 300 ]]; then
        break;
      fi
      if [[ "$tail_line" == "required string" ]]; then
        capture_flag=1
      fi
      if [[ $capture_flag -eq 1 ]]; then
        break;
      fi
    done
  fi
}

您能帮忙找出在300 秒时使尾部和while 循环超时的正确方法吗?谢谢。

【问题讨论】:

    标签: linux bash shell while-loop timeout


    【解决方案1】:

    不活动超时有两个值得考虑的选项。通常,选项 #1 效果更好。

    选项 1:使用超时 (read -t timeout)。

    它将限制“读取”时间。查看来自 bash man 的信息。超时会导致读取失败,打破 whlie 循环。

    在上面的代码中,替换

    tail -n 1 -f "running.log" | while read tail_line
    

     tail -n 1 -f "running.log" | while read -t 300 tail_line
    

    选项 2:TMOUT 环境变量

    通过设置 TMOUT env var 可以达到同样的效果。

    来自 bash man - 'read' 命令:

    -t 超时

    如果没有完整的输入行(或指定数量的字符),则导致读取超时并返回失败 在超时秒内读取。 timeout 可能是一个十进制数 分数 小数点后的部分。此选项仅在 read 正在从终端读取输入时有效, 管道或其他特殊文件;从 常规文件。如果 读取超时,读取将读取的任何部分输入保存到指定的变量名称中。如果超时为 0,则读取返回 立即,无需尝试读取任何数据。退出状态为 0 如果 输入是 在指定的文件描述符上可用,否则为非零。退出状态大于 128,如果 超时。

    【讨论】:

    • 这是个好建议,我已经尝试过第一个选项,read 命令中的-t 选项真的很有用,只有一件事要提,在我的运行中,如果tail 在@987654328 之前@,看起来while read loop 在下标中,所以不知道为什么-t 不起作用,但我检查了其他文档:unix.stackexchange.com/questions/309453/…,它提供了一个解决方案,在while read loop 之后移动tail,然后是额外的@987654333 @ on read 命令对我有用,将在下面发布我的测试结果,谢谢!
    【解决方案2】:

    根据 dash-o 的回答,我对选项 1 进行了测试,read-t 命令仅在主 shell 上的 while read loop 和子 shell 上的 tail 时才能正常工作,在我的问题中,@987654328 @在主shell中,while read loop在子shell中消耗它的输出,在这种情况下,即使为read命令设置-t,脚本在时间用完时不会停止。参考 Monitoring a file until a string is foundBash tail -f with while-read and pipe hangsHow to [constantly] read the last line of a file?

    下面基于dash-o解决方案的工作代码:

    function monitor_log() {
      if [[ -f "running.log" ]]; then
        # Tail the running log last line and keep check required string
        while read -t 300 tail_line
        do
          if [[ "$tail_line" == "required string" ]]; then
            capture_flag=1
          fi
          if [[ $capture_flag -eq 1 ]]; then
            break;
          fi
        done < <(tail -n 1 -f "running.log")
        # Silently kill the remained tail process
        tail_pid=$(ps -ef | grep 'tail' | cut -d' ' -f5)
        kill -13 $tail_pid
      fi
    }
    

    但是作为测试,这个函数在超时自动终止后会让tail进程存活,我们可以通过控制台查看ps -ef观察PID,需要单独kill tail_PID

    还测试另一个解决方案:不要更改tailwhile read loop 的位置,所以tail 仍然在主shell 上,while read loop| 管道之后保留在子shell 中,唯一的变化是添加GNU 的timeout tail 命令之前的命令,它工作完美,超时自动终止后没有 tail 进程离开:

    function monitor_log() {
      if [[ -f "running.log" ]]; then
        # Tail the running log last line and keep check required string
        timeout 300 tail -n 1 -f "running.log" | while read tail_line
        do
          if [[ "$tail_line" == "required string" ]]; then
            capture_flag=1
          fi
          if [[ $capture_flag -eq 1 ]]; then
            break;
          fi
        done
      fi
    }
    

    【讨论】:

    • 注意'while -t 300 read ...'应该是'while read -t 300 ...'
    猜你喜欢
    • 1970-01-01
    • 2013-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-16
    • 1970-01-01
    • 2010-11-30
    相关资源
    最近更新 更多