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

时间:2012-12-18 14:17:12

标签: bash shell ksh

我一直面临着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循环中断在这里没有用。我可以在stdout上看到“检测到Logout完成”以及“Leaks detection complete”,但不是字符串“outside loop”

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

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

3 个答案:

答案 0 :(得分:3)

不要使用while循环,而是使用以下格式:

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

答案 1 :(得分: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完全相同。

答案 2 :(得分:0)

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

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

即。管道关闭时不会立即发送while,即使在其中缓存了未读取的数据,但只有在管道上的后续SIGPIPE尝试时才会发送。{/ p>

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

write()

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

tail -f some_large_file | read one

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

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

此外,如果您坚持使用-f,那么tail可能就足够了,至少可以检测未来事件。

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

cron

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