以下があります。
tail -f
コマンドを使用して、ログファイルで成功メッセージを確認します。コードに出口0があっても、tail -f
プロセスを終了できません。
これでは、スクリプトを完了できません。バッシュでこれを行う他の方法はありますか?
コードは次のようになります。
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
}
私が思いつくことができる最良の答えはこれです
tail -f logfile | read -t 30 line
--pid=$$
で始めます。これにより、bashプロセスが終了したときに終了します。それは私が考えることができるすべてのケースをカバーします(サーバーは出力なしでハングし、サーバーは終了し、サーバーは正しく起動します)。
サーバーの前でテールを開始することを忘れないでください。
tail -n0 -F logfile 2>/dev/null | while read -t 30 line
-F
は、ファイルが存在しない場合でもファイルを「読み取り」ます(表示されたときに読み取りを開始します)。 -n0
はファイル内の既存の内容を読み取らないため、毎回上書きするのではなく、ログファイルに追加して、標準のログローテーションを行うことができます。
編集:
わかりました。テールを使用している場合は、かなり大まかな「ソリューション」です。おそらくテール以外のものを使用したより良い解決策がありますが、私はそれをあなたに与えなければなりません、テールは壊れたパイプからかなりうまくあなたを導きます。 SIGPIPEを処理できる「T型」のほうがおそらくうまく機能します。 Javaプロセスは、ある種の「im alive」メッセージを使用してファイルシステムのドロップをアクティブに実行しているため、待機するのがさらに簡単になります。
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
}
ここで見つけた答えに基づいて、これが私が思いついたものです。
必要なログ出力を確認したら、テールを直接処理して強制終了します。 「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
}
tail man page によると、プロセスが終了した後、tailを終了させることができます
BASHでは、$を使用して、最後に開始したバックグラウンドプロセスのPIDを取得できます。 SO bashを使用している場合:
tail -f --pid=$! logfile
同様の状況で、「開始された」メッセージのログを適切な時間内にテールする必要があり、その時間内にログが見つからない場合は、終了する必要があります。これが私がやったことです。
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
}
これが非常にエレガントであるか、それを行うための最良の方法であるかはわかりませんが、私にとっては十分に機能します。私はどんな意見でも幸せです:)
バックグラウンドプロセスのPIDをキャプチャする
pid=$!
Tailの-pid = PIDオプションを使用して、pid $ PIDを持つプロセスが終了した後に終了するようにします。
tail -f logfile
をバックグラウンドにしてtailpid
をwhile read
ループサブシェルに送信し、trap
をEXITに実装してtail
コマンドを強制終了することができます。
( (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
)
私も同じ問題を抱えていて、シンプルで良い解決策を見つけることができませんでした。私は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()
プロセスを終了する代わりに、代わりにtail -f
プロセスのプロセスIDを見つけて強制終了できます(ログファイルが完了していることが確実であれば、kill -9
を使用しても安全です)。
そうすれば、while read line
は自然に終了し、終了する必要はありません。
または、実際にtail
を使用して画面に出力していないので、より古い学校を試すこともできます。
grep -q 'Started' logfile
while [[ $? -ne 0 ]] ; do
sleep 1
grep -q 'Started' logfile
done
テールプロセスが殺されなかったときに同様の問題がありました
--pid=$!
を使用して終了し、無限のwhileループを開始して、背後にある何かをエコーしてから、背後にあるプロセスが強制終了されたときに強制終了され、テールが強制終了されました。
( while true; do echo 'running'; sleep 5; done ) & ( tail -f --pid=$! log-file )
テールに-fコマンドラインオプションの代わりに無限ループを使用するのはどうですか?
function startServer() {
startJavaprocess > logfile &
while [ 1 ]
do
if tail logfile | grep -q 'Started'; then
echo 'Server started'
exit 0
fi
done
}
Grepにパイプ接続されたtail -n0 -fを使用することは確かに素晴らしい解決策であり、パイプ内の最初のプロセスが実際にデッドgrepプロセスに出力しようとすると停止します。
しかし、最後の現在の出力の近くに表示されるテキストを探している場合、grepはすでに(1ブロックで)テールからの入力全体を読み取っているので、これ以上テキスト出力はありません。 grepが終了する前にパイプを送信する必要があるため、パイプを送信する必要があるログ(または、おそらくパイプバッファーに既にあった)-少なくともこれは私の理解です。
Grepで「-m1」オプションを使用すると、期待どおりに動作し、一致した行の直後に入力を残すように見えますが、違いが生じたり、同様の機能を検索したりするのに役立ちませんでした。パイプバッファーはまだテールからのすべてのテキスト出力を保持しているのではないかと思います。あなたはこのpost-grep-matchテキストを次に出力するために残したかったのは、試みたときに尻尾を殺すため(まだ危険です-なんらかの理由で最後の行になるとどうなりますか?)、制御を呼び出し元のスクリプトに戻します。 。
Grepが終了したら、ログファイルの最後に何かを出力するのが1つの方法であることがわかりました。すなわち。
tail -f logfile | (grep -q; echo >>ログファイル)
私の理論では(私の推測が正しい場合)、パイプをこれよりもバッファーなしで機能させることができます、またはおそらくhuponexit設定コマンドを適切なパイプコンポーネントに追加する-つまり、(おそらく中括弧で)助けて;しかし、ログファイルに空白行を追加することは気にしませんでした。問題なく機能し、テストスクリプトは小さいだけです(そのため、他の処理用のフォーマットに固執する必要のある長期のログファイルはありません)。
shopt -s huponexitは便利ですが、そのサブシェル性のためです。
PS私の最初の投稿はここにあります。何かを繰り返すのではなく、既存の回答へのコメントとしてそれをやりたいと思いますが、今はできないと思います。
これは機能し、サブシェルが死ぬとテールは死ぬはずです
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)
}
元の質問について、なぜexitコマンドが終了しなかったのか、同じ問題が発生し、最終的に原因を見つけました。
Bashのデバッグモードを使用すると、exitコマンドが呼び出されたことがわかりますが、「開始」の直後にログファイルに1行フラッシュするまで、プロセスはハングしています。奇妙なことに、「開始」が出たとき、出口が呼び出されていても、プロセスはまだ何かに引っ掛かっています。 tail -f
、もう1行出てくるまで、フックは本当に解放されます。
したがって、開始後にさらに1行印刷すると、モニターはすぐに終了します。
tail -n0 --pid=$(($BASHPID+1)) -F logfile | sed -n '/Started/{s/.*/Server Started/p; q}'
パイピングの場合、PIDはシーケンシャルなので、テールのpidは$ BASHPIDになり、sedのpidは$ BASHPID + 1になります。 --pidスイッチを指定すると、sedコマンドが終了すると、tailが終了します(正しく!)。このsedコマンドは/ Started /を探し、行全体(。*)を「Server Started」で置き換えて終了します。
前のコマンドをNohupで実行します。
私の場合、Java -jarをNohupで実行します。
Nohup Java -jar trade.jar xx.jar &
ログ出力はありませんが、新しい「Nohup.out」が作成されます。元のログファイルtrade.logも同様に機能します。
その後、tail -f trade.log
、シェルはログ情報を表示し、Ctrl-cはそれを中断でき、シェルに戻ります。
この問題に対する私の好ましい解決策は、「tail」コマンドとそのコンシューマーをサブシェルに入れ、フィルターロジックに親とその子(tailプロセスを含む)を強制終了させることです。プロセスツリーを見ると、次のようになります。
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())
答えを組み合わせて、この簡単な解決策を思いつきました。この例では、Tomcatのstartup.shスクリプトを呼び出してから、catalina.out
「サーバーの起動」がログに記録されるまでログを記録し、その後、テーリングを停止します。
#!/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
末尾を使用しないでください。read
を使用して、同じ「ファイル内の最新のものを監視」できます。
ここでは、ログファイルの代わりに [〜#〜] fifo [〜#〜] を使用しています。
function startServer() {
mkfifo logfile
startJavaprocess > logfile &
a=""; while [ "$a" != "Started" ]; do read <logfile a; done
echo "Server Started"
}
FIFOぶら下がったままになることに注意してください。