我有以下内容。
tail -f
命令检查日志文件以获取成功消息。即使我在代码中有0退出,我也无法结束tail -f
进程。
哪个不让我的脚本完成。在Bash中还有其他方法吗?
代码如下所示。
function startServer() {
touch logfile
startJavaprocess > logfile &
tail -f logfile | while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
exit 0
fi
done
}
答案 0 :(得分:25)
我能想出的最佳答案是
tail -f logfile | read -t 30 line
--pid=$$
开始尾部,这样当bash-process完成时它将退出。它将涵盖我能想到的所有情况(服务器挂起,没有输出,服务器退出,服务器正确启动)。
别忘了在服务器前启动你的尾巴。
tail -n0 -F logfile 2>/dev/null | while read -t 30 line
即使文件不存在,-F
也将“读取”该文件(当文件出现时开始阅读)。 -n0
将不会读取文件中已有的任何内容,因此您可以继续附加到日志文件,而不是每次都覆盖它,以及标准日志轮换。
修改:
好吧,如果你使用尾巴,这是一个相当粗糙的“解决方案”。可能有更好的解决方案使用其他东西,但尾巴,但我得给你,尾巴让你从破裂的管道很好。一个能够处理SIGPIPE的'tee'可能会更好。主动执行某种文件系统丢弃的java进程可能更容易等待。
function startServer() {
touch logfile
# 30 second timeout.
sleep 30 &
timerPid=$!
tail -n0 -F --pid=$timerPid logfile | while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
# stop the timer..
kill $timerPid
fi
done &
startJavaprocess > logfile &
# wait for the timer to expire (or be killed)
wait %sleep
}
答案 1 :(得分:7)
根据我在这里找到的答案,这就是我想出来的。
它直接处理尾部并在我们看到所需的日志输出后将其杀死。使用'pkill -P $$ tail'应确保正确的进程被终止。
wait_until_started() {
echo Waiting until server is started
regex='Started'
tail logfile -n0 -F | while read line; do
if [[ $line =~ $regex ]]; then
pkill -9 -P $$ tail
fi
done
echo Server is started
}
答案 2 :(得分:6)
答案 3 :(得分:2)
捕获后台进程的pid
pid=$!
使用tail的 - pid = PID 选项,以便在pid $ PID终止的进程终止后终止。
答案 4 :(得分:2)
我遇到过类似的情况,我需要在合理的时间内为“已启动”的消息留下日志,如果在此期间找不到,我需要退出。这就是我最终要做的事情。
wait_tomcat_start(){
WAIT=60
echo "Waiting for Tomcat to initialize for $WAIT seconds"
# Tail log file, do a while read loop with a timeout that checks for desired log status,
# if found kill the find and break the loop. If not found within timeout: the read -t will
# kill the while read loop and bounce to the OR statement that will in turn kill the tail
# and echo some message to the console.
tail -n0 -f $SERVERLOG | while read -t $WAIT LINE || (pkill -f "tail -n0 -f" && echo "Tomcat did not start in a timely fashion! Please check status of tomcat!!!")
do
echo "$LINE"
[[ "${LINE}" == *"Server startup in"* ]] && pkill -f "tail -n0 -f" && break
done
}
我不确定这是非常优雅甚至是最好的方法,但它对我来说足够好。我很高兴任何意见:)。
答案 5 :(得分:1)
我遇到了同样的问题,找不到简单而好的解决方案。我不擅长Python,但我设法解决了这个问题:
<强> wait_log.py 强>:
#!/usr/bin/env python
from optparse import OptionParser
import os
import subprocess
import time
def follow(file):
def file_size(file):
return os.fstat(file.fileno())[6]
def is_newLine(line):
return line != None and line.find("\n") != -1;
file.seek(0, os.SEEK_END)
while True:
if file.tell() > file_size(file):
file.seek(0, os.SEEK_END)
line_start = file.tell()
line = file.readline()
if is_newLine(line):
yield line
else:
time.sleep(0.5)
file.seek(line_start)
def wait(file_path, message):
with open(file_path) as file:
for line in follow(file):
if line.find(message) != -1:
break
def main():
parser = OptionParser(description="Wait for a specific message in log file.", usage="%prog [options] message")
parser.add_option("-f", "--file", help="log file")
(options, args) = parser.parse_args()
if len(args) != 1:
parser.error("message not provided")
if options.file == None:
parser.error("file not provided")
wait(options.file, args[0])
if __name__ == "__main__":
main()
答案 6 :(得分:1)
可以后台tail -f logfile
,将tailpid
发送到while read
循环子shell,并在EXIT上实现trap
以终止tail
命令。< / p>
( (sleep 1; exec tail -f logfile) & echo $! ; wait) | (
trap 'trap - EXIT; kill "$tailpid"; exit' EXIT
tailpid="$(head -1)"
while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
exit 0
fi
done
)
答案 7 :(得分:1)
有一个类似的问题,尾部进程在
时没有被杀死使用--pid=$!
来杀死它并启动一个无限的while循环来回显尾部之前的东西,当底层进程被杀死并因此杀死尾部时会被杀死。
( while true; do echo 'running'; sleep 5; done ) & ( tail -f --pid=$! log-file )
答案 8 :(得分:1)
如何使用无限循环代替尾部的-f命令行选项?
function startServer() {
startJavaprocess > logfile &
while [ 1 ]
do
if tail logfile | grep -q 'Started'; then
echo 'Server started'
exit 0
fi
done
}
答案 9 :(得分:1)
您可以找到tail -f
进程的进程ID而不是退出进程,而是将其终止(如果您确定日志文件已完成,则kill -9
在这里甚至可以安全)
这样,while read line
将自然终止,您无需退出。
或者,既然你没有真正使用tail
输出到屏幕,你也可以尝试更老的学校:
grep -q 'Started' logfile
while [[ $? -ne 0 ]] ; do
sleep 1
grep -q 'Started' logfile
done
答案 10 :(得分:0)
使用tail -n0 -f piped to grep确实是一个不错的解决方案,实际上管道中的第一个进程在尝试输出到死grep进程时会死掉。
但是如果你正在寻找出现在尾部的最后一个当前输出附近的文本,那么grep已经从尾部读取了整个输入(在一个块中),因此将不再有任何文本日志中输出需要向下发送管道,因为grep在退出之前已经读过它(或者它已经在管道缓冲区中) - 至少这是我的理解。
在grep上使用“-m1”选项看起来它完全符合你的要求,并在匹配的行之后立即保留输入,但它似乎没有什么区别或帮助我搜索类似的功能。我怀疑管道缓冲区仍然保留尾部的所有文本输出,或者其他一些原因导致尾部没有任何剩余的输出。你希望这个后grep-match文本仍然可以在下一次输出,因为它会在尝试时杀死你的尾巴(仍有风险 - 如果出于某种原因最后一行会发生什么?),并将控制返回到调用脚本
我发现一种方法是在grep退出后将任何内容输出到日志文件的末尾;即
tail -f logfile | (grep -q; echo&gt;&gt; logfile)
我有一个理论(如果我的猜测是正确的)你可以强制管道减少缓冲以使其无需工作,或者可能将huponexit设置命令添加到适当的管道组件 - 即(可能是卷曲的) )括号会有所帮助;但是我并不关心在日志文件中添加一个空行,它工作正常,它只是一个较小的测试脚本(所以不是一个需要坚持其他处理格式的长期日志文件)。
shopt -s huponexit会很有用,但对于它的小部分内容。
PS我在这里的第一篇文章,会喜欢把它作为对现有答案的评论,而不是重复迭代,但我认为我现在不能。
答案 11 :(得分:0)
这应该可以工作,一旦子shell死亡,尾部应该死掉
function startServer() {
touch logfile
startJavaprocess > logfile &
while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
exit 0
fi
done < <(tail -f logfile)
}
Try this:
function startServer() {
while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
return 0
fi
done < <(startJavaprocess | tee logfile)
}
答案 12 :(得分:0)
对于原始问题,为什么退出命令没有退出,我得到了同样的问题,最后找到了原因。
通过使用bash的调试模式,我可以看到exit命令被调用但是进程仍然挂起,直到在“Started”之后又有一行刷新到日志文件。奇怪的是,当'Started'出现时,即使退出已被调用,该过程仍然被某些东西所吸引。我想这是tail -f
,直到另外一行出来,它才真正释放钩子。
因此,如果您在启动后再打印一行,您的显示器将立即退出。
答案 13 :(得分:0)
不要使用尾巴 - 你可以使用read
获得相同的'监控文件中的最新内容'。
这里我使用FIFO代替日志文件:
function startServer() {
mkfifo logfile
startJavaprocess > logfile &
a=""; while [ "$a" != "Started" ]; do read <logfile a; done
echo "Server Started"
}
请注意,这会留下一个FIFO。
答案 14 :(得分:0)
我对此问题的首选解决方案是将'tail'命令及其使用者置于子shell中,并让过滤器逻辑终止父项及其子项(包括尾部过程)。如果您查看进程树,它将是:
startServer (pid=101)
startServer (pid=102) << This is the subshell created by using parens "(...)"
tail -f logfile (pid=103) << Here's the tail process
startServer (pid=104) << Here's the logic that detects the end-marker
在这种方法中,结束标记检测逻辑(pid 104)查找其父PID(102)及其所有子项,并杀死整个批次 - 包括其自身。然后祖父母(上面的pid 101)可以自由继续。
function startServer() {
touch logfile
startJavaprocess > logfile &
tail -f logfile | while read line
do
if echo $line | grep -q 'Started'; then
echo 'Server Started'
mypid=$BASHPID
pipeParent=$(awk '/^PPid/ {print $2}' /proc/$mypid/status)
kill -TERM $pipeParent $(pgrep -P $pipeParent) # Kill the subshell and kids
fi
done
}
# To invoke startServer(), add a set of parens -- that puts it in a subshell:
(startServer())
答案 15 :(得分:0)
结合使用答案,我想出了这个简单的解决方案。此示例调用Tomcat的startup.sh脚本,然后关闭catalina.out
日志,直到“Server startup”被记录,然后停止拖尾。
#!/bin/bash
function logUntilStarted() {
tail -n0 -F /home/tomcat/logs/catalina.out | while read line; do
if echo $line && echo $line | grep -q 'Server startup' ; then
pkill -9 -P $$ tail > /dev/null 2>&1
fi
done
}
/home/tomcat/bin/startup.sh
logUntilStarted
答案 16 :(得分:0)
tail -n0 --pid=$(($BASHPID+1)) -F logfile | sed -n '/Started/{s/.*/Server Started/p; q}'
当管道时,PID是顺序的,因此尾部的pid将是$ BASHPID,而sed的pid将是$ BASHPID + 1。当sed命令退出时, - pid开关将导致tail退出(正确!)。这个sed命令将查找/ Started /然后用“Server Started”替换整行(。*),然后退出。
答案 17 :(得分:0)
使用nohup运行上一个命令。
在我的情况下,使用nohup运行java -jar,例如
nohup java -jar trade.jar xx.jar &
没有日志输出,但是将创建一个新的“ nohup.out”。原始日志文件trade.log也可以正常工作。
然后tail -f trade.log
,shell将显示日志信息,Ctrl-c可以中断它,然后返回shell。