与同时结合在子shell使用尾/断线不会退出循环(Using tail in a subshell

2019-10-17 12:19发布

我一直面临着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。

可有人请阐明如何做到这一点一些轻?

Answer 1:

试试这个,虽然它不是完全一样(不跳过启动日志文件的开头):

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



Answer 2:

相反管道进入while循环,使用这种格式来代替:

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


Answer 3:

你的功能在一定意义上的作品,但你不会注意到它,直到另一条线被写入文件触发字已被发现之后 。 这是因为tail -5 -f通常可以写的所有文件到管道的最后五行的一个write()调用,并继续书写新的生产线都在一个电话,这样就不会被发送SIGPIPE信号,直到它会尝试写入管道while退出循环。

所以,如果你的文件定期增长那么应该不会有问题,但如果它是更常见的文件停止生长触发字写入它只是后,那么你的守望脚本也将挂起,直到任何新的输出写入该文件。

SIGPIPE当管道被关闭不会立即发送,即使在它的缓冲未读取数据,但只有当后续write()在管道上尝试。

这可以很简单地证明。 此命令将不会退出(提供的文件的尾部小于管尺寸的缓冲器),直到手动中断,或者您写入文件一个更多字节:

tail -f some_large_file | read one

但是,如果你强迫尾部进行多次写入管道,并最终写入前读者退出确认,那么一切都将如预期:

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你需要在读取触发词做任何处理语句。



文章来源: Using tail in a subshell in conjunction with while/break does not exit the loop
标签: bash shell ksh