即使满足条件也无法打破 while read 循环

即使满足条件也无法打破 while read 循环

这应该很简单,但我缺少一些东西,需要一些帮助。我的要求是通过 tail 读取日志文件以获取最新日志, grep 下载配置并复制所有文件并将其写入 MyOwnLogFile.log 但我希望一旦 .myworkisdone 文件出现在 /usr/local/FOLDER 中就停止此操作/ 有一点是肯定的,当所有日志完成后,.myworkisdone 将在最后生成……但脚本只是继续读取日志文件,并且永远不会退出它,即使该文件已创建。

while [[ ! -e /usr/local/FOLDER/.myworkisdone ]];
do
    sudo tail -f -n0  /var/log/server22.log | while read line; do echo "$line" | grep -e 'Downloading Config’ -e ‘Copying all files of' ; done  >> /var/tmp/MyOwnLogFile.log
done

我也尝试until而不是 while 检查文件,但脚本仍然无法打破读取日志文件的咒语。先感谢您。

答案1

正如@ikkachu 所说,tail -f这是一个无限的过程。但是,您可以使用内部循环来启动中断:

while :
do
    tail -f server.log | while IFS= read -r line
    do
        printf '%s\n' "$line" | grep 'word' >> ownlog.log
        if [ -e killer.file ] ; then break ; fi
    done
    break 
done
  • 为什么break 2在 if 循环中不起作用?

管道启动一个子shell,该break命令无法退出该子shell。因此break在外循环中还有另一个。

  • 脚本什么时候停止?

一旦killer.file存在,待处理| while read ...仍会等待另一个条目并保持整个打开状态。即您需要下一行,即(使用虚拟文件):

 echo test >> server.log
 #"normal" output - nothing happens

 echo word >> server.log
 #matching output - logged to ownlog.log
 
 touch killer.file
 #killer file appears - breaks cannot take effect
 #as "read" still awaits signal.

 echo test2 >> server.log
 #break takes effect

killer.file如果还表明没有更多日志写入server.log,因此进程保持持续打开状态,这一点尤其重要。

答案2

您需要将“停止文件”的检查与日志文件的读取分开,因为tail -f不会退出。以下 shell 脚本通过让后台任务每秒检查一次“停止文件”是否存在来实现此目的。每当文件出现时,它都会tail -f用一个信号终止进程(与停止读取PIPE时收到的信号相同)。greptail -f由于PIPE信号而终止时,grep进程也会终止,因为其输入流关闭。

#!/bin/sh

stopfile=/usr/local/FOLDER/.myworkisdone

set -- tail -f /var/log/server22.log 

"$@" | {
        while true; do
                if [ -e "$stopfile" ]; then
                        pkill -PIPE -fx "$*"
                        break
                fi
                sleep 1
        done &

        grep -e 'Downloading Config' \
             -e 'Copying all files of'
} >/var/tmp/MyOwnLogFile.log

这会将位置参数设置为tail -f命令。这使得以后参考它更容易一些pkillpkill使用两者来调用该实用程序-f-x以便能够准确地终止我们之前启动的命令。

该值"$*"是一个字符串,由所有位置参数(命令tail -f)组成,参数之间有一个空格(默认情况下)。

使用命令bash的命名数组tail -f

#!/bin/bash

stopfile=/usr/local/FOLDER/.myworkisdone

talicmd=( tail -f /var/log/server22.log )

"${tailcmd[@]}" | {
        while true; do
                if [ -e "$stopfile" ]; then
                        pkill -PIPE -fx "${tailcmd[*]}"
                        break
                fi
                sleep 1
        done &

        grep -e 'Downloading Config' \
             -e 'Copying all files of'
} >/var/tmp/MyOwnLogFile.log

grep(我还注意到问题中使用的正则表达式周围的几个单引号不是普通的单引号,而是“花哨的引号”。)

答案3

perl 有一个模块叫做文件::尾巴旨在监视日志文件,因此您的程序可以在满足条件时采取操作(例如,如果匹配模式则打印该行)。

     #!/usr/bin/perl

     use File::Tail;
     tie *FH,"File::Tail",(name=>shift,nowait=>1);

     while (<FH>) {
         print if (m/Downloading Config|Copying all files of/);
         exit if ( -e '/usr/local/FOLDER/.myworkisdone')
     }

另存为,例如,watch.pl使其可执行chmod +x watch.pl并运行为:

$ ./watch.pl /var/log/server22.log

或者只是将其作为一行代码嵌入到 shell 脚本中,与 awk 或 sed 脚本相同:

perl -MFile::Tail -e 'tie *FH,"File::Tail",(name=>shift,nowait=>1);
      while (<FH>) {
        print if (m/Downloading Config|Copying all files of/);
        exit if ( -e '/usr/local/FOLDER/.myworkisdone')
      }' /var/log/server22.log

这将监视名为脚本第一个参数的日志文件 ( name=>shift)。它打印与该模式匹配的所有日志文件行,如果 .myworkisdone 文件存在则退出。

nowait=>1防止脚本在读取输入文件时阻塞。虽然不太理想,但对于快速而肮脏的脚本来说还可以。更好的解决方案是使用 File::Tail 的select()方法。看man File::Tail

顺便说一句,该select()方法还可以轻松地同时监视多个日志文件。

相关内容