在bash脚本中使用tail -f命令和grep命令查看日志文件内容

3

我想创建一个脚本,在正在写入的日志文件中搜索特定字符串。我想获取第一个结果并将其放入变量以供以后使用。这将通过SSH连接使用,如下所示:

ssh 'user@xxx.xxx.xxx.xxx' 'bash -s' < /usr/local/bin/checklog.sh string

常规终端中的命令

tail -f /var/log/named.log | grep $1 > $var
echo "${var}"

当我尝试上述方法时,没有任何输出。

我该如何将其放入脚本中?我尝试过那种方式,但它没有输出。 - J.Milliscone
2
tail -f file | grep ...永远不会退出,因此您的echo语句永远不会执行。 - larsks
tail -f会在文件消失的情况下退出,但除此之外,它会一直运行下去。 - Etan Reisner
有道理。当我去掉 > $var 后,我本以为它至少会打印出查找结果。但实际上并没有。问题在于我正在对文件进行写入,而同时又在对该字符串进行 grep 操作。我是否可以添加一些逻辑来使其退出,或者需要考虑不同的方法? - J.Milliscone
1
“> $var” 的意思是“将标准输出重定向到文件$var”,你还期望什么呢? - Siguza
3个回答

3
使用while循环可能适用于您的情况,但请注意它不能保证捕获日志文件中的每一行。考虑这样一种情况,日志编写器包含一个动作,会写出两行内容:
Something bad just happened:\nError xyz on line 22

很可能当您执行tail -1操作时,您的循环只会看到第二行。
而且,while循环实现意味着您在循环中旋转CPU,不断触发tail命令(当while实现运行时,请查看top,与tail -f相比)。
如果您只想在匹配模式后停止监视,则此问题提供了一些好的建议。(请注意tail进程存在的问题。)
这个怪物可能不是最优的,但它可以捕获每一行,在等待新行时使用最少的CPU,完成后终止tail,并为您编写额外逻辑(例如基于不同匹配模式执行操作)提供了灵活性:
watchPattern=$1
logFile=/var/log/named.log
logLine=""

while read -r logLine ; do
    #Do we have a match?
    if [[ "$logLine" == *"$watchPattern"* ]] ; then
        #Confirmation message, written to console (for example, not needed)
        echo "Found a match."
        #Kill off the tail process  (a bit of a hack that assumes one at a time)
        kill $(ps -eo pid,command | awk -v pattern="tail -fn0 $logFile" '$0 ~ pattern && !/awk/ {print $1}')
        #Get out of here
        break
    fi
done< <(exec tail -fn0 "$logFile")

#logLine will be the matched value
echo "match = $logLine"

1
另外,顺便说一下,对于仅包含-e(使用GNU而不是POSIX echo)或-n的行,“echo”命令可能无法正常工作。此外,XSI-扩展的POSIX echo默认会在此处展开反斜杠转义序列,这可能不是您想要的。请考虑使用printf '%s\n' "$logLine"代替 - 并查看http://pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html的“应用程序使用”和“原理”部分以获取有趣的阅读材料。 - Charles Duffy
嗯。明确杀死“tail”进程的做法很有趣,顺便说一句。当它在写入标准输出时收到SIGPIPE信号时,它应该自动终止,但如果事件之间的时间足够长,也许这样做是值得的。 - Charles Duffy
如果不会因此而付出正确性的惩罚(冒着杀死错误进程的风险),那我同意这个建议。也许你可以用一个唯一的(通过mktemp创建的)锁文件来启动它,然后使用“fuser -k”命令来杀死所有与该文件句柄相关联的进程? - Charles Duffy
非常正确。正如脚本中所提示的,它只在您知道将串行使用该命令时才有效。那么,解决它的问题!在执行(最后一个“)”之后只需添加“&& pid=$!”,然后在跳出循环后“kill $pid”是否可以解决它? - bto
这不允许$!被任何东西覆盖,除了"异步列表",它明确引用了“&” 的使用。因此,通过这种扩展,bash 可能不符合 POSIX sh的规范。并不是说我期望它会改变 - bash 的 echo 实现一直没有遵循规范,有许多脚本依赖于它。 - Charles Duffy
显示剩余5条评论

1

> $var 不是你想象中的那样。
它将前一个命令的输出重定向到一个以 $var 内容为名称的文件中。
要捕获命令的输出并将其放入变量中,请使用 variableName="$(...)"

var="$(tail -f /var/log/named.log | grep $1)"

-1

感谢大家的建议。你们帮助我找到了更好的方法,使用while和tail而不是-f。

werd=$1
var=""
while [ "${var}" == "" ]
do
var=$(tail -1 /var/log/named.log | grep "${werd}");
done
echo "${var}";

这只是读取文件中的最后一行。由于文件正在被写入,因此最后一行会发生变化,这正是我要寻找的结果。


FYIпјҢ==еңЁPOSIX testпјҲд№ҹз§°дёә[пјүдёӯж— ж•Ҳпјӣе”ҜдёҖж ҮеҮҶдҝқиҜҒзҡ„еӯ—з¬ҰдёІжҜ”иҫғиҝҗз®—з¬ҰжҳҜ=гҖӮ - Charles Duffy
这在性能方面非常昂贵,顺便说一下;即使超越了竞争条件(如果正在快速添加行,则可能会错过行),我也不能向其他人推荐它。 - Charles Duffy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接