1

我一直面临着一个非常特殊的 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创建子外壳有关。我想要做的是,退出该子外壳并退出 Script2 以将控制权返回给 Script1。

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

4

3 回答 3

3

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

while read line
do
   # put loop body here
done < <(tail -5f ${logfile})
于 2012-12-18T15:13:36.877 回答
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

于 2012-12-18T16:54:38.000 回答
0

您的函数在某种意义上是有效的,但是在找到触发词之后将另一行写入文件之前,您不会注意到它这样做了。这是因为tail -5 -f通常可以在一次调用中将文件的最后五行全部写入管道,并在一次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语句替换为读取触发短语时需要执行的任何处理。

于 2012-12-18T21:22:51.763 回答