繁体   English   中英

在子shell中使用tail和while / break不会退出循环

[英]Using tail in a subshell in conjunction with while/break does not exit the loop

我一直面临着shell脚本的一个非常奇怪的问题。

这是场景

Script1(在后台生成) - > Script2

Script2具有以下代码

function check_log()
{
    logfile=$1
    tail -5f ${logfile} | while read line
    do
      echo $line
      if echo $line|grep "${triggerword}";then
        echo "Logout completion detected"
        start_leaks_detection
        triggerwordfound=true
        echo "Leaks detection complete"
      fi
      if $triggerwordfound;then
        echo "Trigger word found and processing complete.Exiting"
        break
      fi

    done
        echo "Outside loop"
        exit 0

}

check_log "/tmp/somefile.log" "Logout detected"

现在,while循环中断并没有帮助。 我可以看到“检测到注销完成”以及“泄漏检测完成”在标准输出上回显,但不是字符串“外部循环”

我假设这必须用tail -f创建一个子shell。 我想要做的是,退出子shell并退出Script2以控制回Script1。

有人可以说明如何做到这一点吗?

而不是管道进入你的while循环,而是使用这种格式:

while read line
do
   # put loop body here
done < <(tail -5f ${logfile})

试试这个,虽然它不完全相同(它不会在启动时跳过日志文件的开头):

triggerwordfound=
while [ -z "$triggerwordfound" ]; do
    while read line; do
        echo $line
        if echo $line|grep "${triggerword}";then
            echo "Logout completion detected"
            start_leaks_detection
            triggerwordfound=true
            echo "Leaks detection complete"
        fi
    done
done < "$logfile"
echo "Outside loop"

双循环有效地与tail -f做同样的事情。

从某种意义上说,您的函数是有效的,但是在找到触发器字之后,在将另一行写入文件之前,您不会注意到它。 这是因为tail -5 -f通常可以在一次write()调用中将文件的最后五行write()管道并继续在一次调用中写入新行,因此直到它才会发送SIGPIPE信号直到它会在while循环退出尝试写入管道。

因此,如果您的文件经常增长,那么应该没有问题,但如果您的文件在写入触发器字后立即停止增长更常见,那么您的观察器脚本也会挂起,直到写入任何新输出到文件。

即管道关闭时,即使在其中缓存了未读取的数据,也不会立即发送SIGPIPE ,但仅在尝试管道上的后续write()时才发送。

这可以非常简单地证明。 此命令不会退出(假设文件的尾部小于管道大小的缓冲区),直到您手动中断它,或者再向文件写入一个字节:

tail -f some_large_file | read one

但是,如果强制tail对管道进行多次写入并确保读取器在最终写入之前退出,那么一切都将按预期工作:

tail -c 1000000 some_large_file | read one

不幸的是,在给定系统上发现管道缓冲区的大小并不总是很容易,也不总是只能在文件中已经存在多个管道缓冲区的数据时才开始读取文件,并且触发字已经是在文件中,至少是文件末尾的管道缓冲区大小字节。

不幸的是, tail -F (你应该使用它而不是-f )也不会尝试每5秒写一个零字节,否则这可能会以更有效的方式解决你的问题。

此外,如果您要坚持使用tail ,那么-1可能就足够了,至少对于检测任何未来事件而言。

顺便说一句,这是一个稍微改进的实现,仍然使用tail因为我认为这可能是你最好的选择(你总是可以用cron或类似的方法在日志中添加一个周期标记行(大多数syslogd实现也有内置的标记功能)来保证您的函数将在标记期间返回:

check_log ()
{
        tail -1 -F "$1" | while read line; do
                case "$line" in
                *"${2:-SOMETHING_IMPOSSIBLE_THAT_CANNOT_MATCH}"*)
                        echo "Found trigger word"
                        break
                        ;;
                esac
        done
}

echo语句替换为读取触发器短语时需要执行的任何处理。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM