文字列が見つかるまでファイルを監視する


60

tail -fを使用して、アクティブに書き込まれているログファイルを監視しています。特定の文字列がログファイルに書き込まれたら、監視を終了し、残りのスクリプトを続行します。

現在、私は使用しています:

tail -f logfile.log | grep -m 1 "Server Started"

文字列が見つかると、grepは期待どおりに終了しますが、スクリプトを続行できるように、tailコマンドも終了させる方法を見つける必要があります。


元のポスターがどのオペレーティングシステムで実行されていたのだろうか。Linux RHEL5システムでは、grepコマンドが一致を検出して終了すると、tailコマンドが終了するだけで驚いた。
ザスター2013

4
@ZaSter:tail次の行でのみ死にます。これを試してください:date > log; tail -f log | grep -m 1 triggerその後、別のシェルで:最初のシェルでecho trigger >> log出力が表示さtriggerれますが、コマンドは終了しません。次にdate >> log、2番目のシェルで試してください。最初のシェルのコマンドは終了します。しかし、これで手遅れになることもあります。トリガーラインの後のラインが完了したときではなく、トリガーラインが表示されたらすぐに終了する必要があります。
アルフェ

@Alfeという優れた説明と例です。
-ZaSter

1
エレガントな1行の堅牢なソリューションは、00prometheusの答えのような+使用するtailgrep -q
トレバーボイドスミス

回答:


41

シンプルなPOSIXワンライナー

シンプルなワンライナーです。bash固有のトリックや非POSIXトリック、または名前付きパイプさえ必要ありません。あなたが本当に必要なのは、終了切り離すことであるtailからにgrep。これにより、一度grep終了すると、スクリプトtailはまだ終了していなくても続行できます。したがって、次の簡単な方法で目的を達成できます。

( tail -f -n0 logfile.log & ) | grep -q "Server Started"

grep文字列が見つかるまでブロックし、すぐに終了します。tail独自のサブシェルから実行することにより、独立して実行されるようにバックグラウンドに配置できます。一方、メインシェルは、grep終了するとすぐにスクリプトの実行を継続できます。tail次の行がログファイルに書き込まれるまでサブシェルに残り、その後終了します(メインスクリプトが終了した後でも可能です)。主なポイントは、パイプラインtailが終了するのを待機しないため、パイプラインが終了するとすぐにgrep終了することです。

いくつかのマイナーな調整:

  • オプション-n0を指定tailすると、ログファイルの文字列が以前に存在する場合に、ログファイルの現在の最終行から読み取りを開始します。
  • tail-fではなく-F を指定することもできます。POSIXではありませんがtail、待機中にログがローテーションされても機能します。
  • -m1ではなくオプション-qを使用するとgrep、最初の出現後に終了しますが、トリガー行は出力されません。また、POSIXであり、-m1はそうではありません。

3
このアプローチにより、tail実行は永久にバックグラウンドで実行されます。tailバックグラウンドのサブシェル内でPIDをどのようにキャプチャし、メインシェルに公開しますか?を使用して、セッションに接続tailされているすべてのプロセスを強制終了することで、サブオプションの回避策を思いつくことができpkill -s 0 tailます。
リック・ファン・デル・ズウェート

1
ほとんどのユースケースでは、問題になりません。そもそもこれを行う理由は、ログファイルにより多くの行が書き込まれることを期待しているためです。tail壊れたパイプに書き込もうとするとすぐに終了します。パイプgrepは完了するとすぐに壊れるので、一度grep完了tailすると、ログファイルにもう1行追加されると終了します。
00プロメテウス

私はこのソリューションを使用したとき、私背景をしませんでしtail -f
トレバーボイドスミス

2
@Trevor Boyd Smith、はい、ほとんどの状況で動作しますが、OPの問題は、grepが終了するまでgrepが完了せず grepが終了した(tailが試行したとき)ログファイル別の行が表示されるまでtailが終了しないことでしたgrepの終了によって破損したパイプラインにフィードします)。したがって、バックグラウンドテールがない限り、grepがキャッチする行ではなく、ログファイルに余分な行が現れるまでスクリプトの実行は継続されません。
-00prometheus

Re [トレイルは、[必要な文字列パターン]の後に別の行が表示されるまで終了しません]:それは非常に微妙であり、私は完全にそれを見逃しました。探していたパターンが真ん中にあり、すべてがすばやく印刷されたため、気が付きませんでした。(再びあなたが説明する動作は非常に微妙です)
トレバーボイドスミス

59

受け入れられた答えは私には機能しませんが、混乱を招き、ログファイルが変更されます。

私はこのようなものを使用しています:

tail -f logfile.log | while read LOGLINE
do
   [[ "${LOGLINE}" == *"Server Started"* ]] && pkill -P $$ tail
done

ログ行がパターンと一致する場合、tailこのスクリプトによって開始されたものを強制終了します。

注:出力を画面にも表示する場合| tee /dev/ttyは、whileループでテストする前に行をエコーし​​ます。


2
これは機能しpkillますが、POSIXでは指定されておらず、どこでも利用できるわけではありません。
リチャードハンセン

2
whileループは必要ありません。-gオプションを付けてwatchを使用すると、厄介なpkillコマンドを省くことができます。
l1zard

@ l1zardそれを肉付けできますか?特定の行が表示されるまで、ログファイルの末尾をどのように監視しますか?(あまり重要ではありませんが、watch -gが追加されたときも興味があります。そのオプションを備えた新しいDebianサーバーと、それを持たない別の古いRHELベースのサーバーがあります)。
ロブ・ウィーラン

ここでなぜ尾が必要なのか、私にははっきりしていません。私がこれを理解している限り、ユーザーはログファイルに特定のキーワードが表示されたときに特定のコマンドを実行したいと考えています。以下のwatchを使用したコマンドは、まさにこのタスクを実行します。
l1zard

完全ではありません-特定の文字列がログファイルに追加されたときにチェックします。これは、TomcatまたはJBossが完全に起動したときのチェックに使用します。発生するたびに「サーバーが起動しました」(または同様の)を書き込みます。
ロブウェラン

16

Bashを使用している場合(少なくとも、しかしPOSIXで定義されていないように見えるため、一部のシェルでは欠落している可能性があります)、次の構文を使用できます。

grep -m 1 "Server Started" <(tail -f logfile.log)

既に述べたFIFOソリューションとほとんど同じように機能しますが、書くのははるかに簡単です。


1
この作品が、尾はまだあなたが送っそれまでは動作してSIGTERMます(Ctrl + C、exitコマンドを、またはそれを殺す)
MEMS

3
@mems、ログファイルの追加の行はすべて行います。がそれtailを読み、それを出力しようとし、それを終了するSIGPIPEを受け取ります。したがって、原則としてあなたは正しいです。tailログファイルに何も書き込まれないと、無期限に実行される可能性があります。実際には、これは多くの人々にとって非常にきちんとした解決策かもしれません。
アルフェ

14

tail終了するには、いくつかの方法があります。

不十分なアプローチ:tail別の行を強制的に書き込む

一致を見つけてtail終了した直後に、出力の別の行を強制的に書き込むことができgrepます。これによりtail、が取得され、SIGPIPE終了します。これを行う1つの方法はtailgrep終了後に監視対象のファイルを変更することです。

コードの例を次に示します。

tail -f logfile.log | grep -m 1 "Server Started" | { cat; echo >>logfile.log; }

この例でcatgrep、stdoutを閉じるまで終了しtailないためgrep、stdinを閉じる前にパイプに書き込むことができません。 未変更catの標準出力を伝播するために使用されgrepます。

このアプローチは比較的単純ですが、いくつかの欠点があります。

  • 場合はgrep閉じは標準入力を閉じる前にstdout、常に競合状態が存在します: grep閉じSTDOUT、トリガーcatトリガー、出口へechoのトリガ、tail出力に行を。この行がgrep前に送信された場合、grepstdinを閉じる機会tailがありましたが、SIGPIPE別の行を書き込むまで取得しません。
  • ログファイルへの書き込みアクセスが必要です。
  • ログファイルを変更しても問題はありません。
  • 別のプロセスと同時に書き込みを行うと、ログファイルが破損する可能性があります(書き込みがインターリーブされ、ログメッセージの途中に改行が表示される場合があります)。
  • このアプローチは固有のものtailです。他のプログラムでは機能しません。
  • 第三のパイプラインステージは、(あなたのようなPOSIXの拡張機能を使用している場合を除き、それは硬質第2パイプラインステージのリターン・コードへのアクセスを取得することができますbashPIPESTATUS配列)。この場合、grep常に0を返すため、これは大したことではありませんが、一般に、中間段階は、戻りコードを気にする別のコマンドに置き換えられる可能性があります(たとえば、「サーバー開始」が検出され、 「サーバーの起動に失敗しました」が検出された場合)。

次のアプローチでは、これらの制限を回避します。

より良いアプローチ:パイプラインを避ける

FIFOを使用してパイプラインを完全に回避することができ、grep戻った後も実行を継続できます。例えば:

fifo=/tmp/tmpfifo.$$
mkfifo "${fifo}" || exit 1
tail -f logfile.log >${fifo} &
tailpid=$! # optional
grep -m 1 "Server Started" "${fifo}"
kill "${tailpid}" # optional
rm "${fifo}"

コメントでマークされた行は# optional削除でき、プログラムは引き続き動作します。tail入力の別の行を読み取るか、他のプロセスによって強制終了されるまで、そのまま残ります。

このアプローチの利点は次のとおりです。

  • ログファイルを変更する必要はありません
  • このアプローチは、他のユーティリティでも機能します tail
  • 競合状態に悩まされない
  • grep(または使用している代替コマンド)の戻り値を簡単に取得できます。

このアプローチの欠点は、特にFIFOの管理の複雑さです。一時ファイル名を安全に生成する必要があり、ユーザーが途中でCtrl-Cを押しても一時FIFOが削除されるようにする必要があります。スクリプト。これは、トラップを使用して実行できます。

代替アプローチ:メッセージを送信して殺す tail

あなたは得ることができますtailようにそれに信号を送信することにより、出口へのパイプラインステージをSIGTERM。課題は、コード内の同じ場所にある2つのことを確実に知ることです: tailのPIDとgrep終了したかどうか。

のようなパイプラインtail -f ... | grep ...を使用すると、最初のパイプラインステージを簡単に変更して、tailバックグラウンド化tailおよび読み取りを行うことで変数にPID を保存できます$!。また、2番目のパイプラインステージを変更して、終了kill時に実行することも簡単grepです。問題は、パイプラインの2つのステージが(POSIX標準の用語で)別個の「実行環境」で実行されるため、2番目のパイプラインステージが最初のパイプラインステージによって設定された変数を読み取れないことです。シェル変数を使用せずに、2番目のステージが何らかの形でtailのPIDを把握してtailgrep戻るときに強制終了できるようにするか、最初のステージに何らかの方法でgrep戻るときに通知する必要があります。

2番目のステージはのPID pgrepを取得するために使用できますtailが、それは信頼性が低く(間違ったプロセスと一致する可能性があります)、移植性pgrepがありません(POSIX標準で指定されていません)。

最初のステージは、PIDを入力echoすることでパイプを介して2番目のステージにPIDを送信できますが、この文字列はtailの出力と混合されます。2つの逆多重化には、の出力に応じて、複雑なエスケープスキームが必要になる場合がありtailます。

FIFOを使用して、grep終了時に2番目のパイプラインステージから最初のパイプラインステージに通知することができます。その後、最初の段階で殺すことができtailます。コードの例を次に示します。

fifo=/tmp/notifyfifo.$$
mkfifo "${fifo}" || exit 1
{
    # run tail in the background so that the shell can
    # kill tail when notified that grep has exited
    tail -f logfile.log &
    # remember tail's PID
    tailpid=$!
    # wait for notification that grep has exited
    read foo <${fifo}
    # grep has exited, time to go
    kill "${tailpid}"
} | {
    grep -m 1 "Server Started"
    # notify the first pipeline stage that grep is done
    echo >${fifo}
}
# clean up
rm "${fifo}"

このアプローチには、以前のアプローチのすべての長所と短所がありますが、より複雑です。

バッファリングに関する警告

POSIXでは、stdinストリームとstdoutストリームを完全にバッファリングできます。これは、tailの出力がgrep任意の長い時間処理されない可能性があることを意味します。GNUシステムでは問題はないはずです。GNUはすべてのバッファリングを回避するGNUをgrep使用しread()、GNU はstdout tail -fへのfflush()書き込み時に通常の呼び出しを行います。GNU以外のシステムは、バッファーを無効にしたり定期的にフラッシュしたりするために特別なことをしなければならない場合があります。


あなたのソリューション(他の人のように、私はあなたを責めない)は、あなたの監視が始まる前にログファイルに既に書き込まれたものを見逃すでしょう。tail -f出力のみ最後の10行をした後、次のすべてになります。これを改善-n 10000するには、末尾にオプションを追加して、最後の10000行も出力されるようにします。
アルフェ

別のアイデア:fifoの出力をtail -ffifoに渡してgrepすることで、fifoソリューションを修正 できると思いますmkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f
アルフェ

@Alfe:私は間違っている可能性がありtail -f logますが、FIFOに書き込むと、一部のシステム(GNU / Linuxなど)で行ベースのバッファリングではなくブロックベースのバッファリングが使用されるとgrep考えられます。ログに表示されます。システムはstdbuf、GNU coreutils などから、バッファリングを変更するユーティリティを提供する場合があります。ただし、このようなユーティリティは移植性がありません。
リチャードハンセン

1
@Alfe:実際には、POSIXは端末と対話する場合を除いてバッファリングについて何も言っていないように見えるので、標準の観点からは、あなたの単純なソリューションは私の複雑なソリューションと同じくらい良いと思います。ただし、各ケースでさまざまな実装が実際にどのように動作するかについては、100%確信はありません。
リチャードハンセン

実際、私は今、grep -q -m 1 trigger <(tail -f log)他の場所で提案されているより単純なものを探して、tail必要なよりもバックグラウンドで1行長く実行するという事実で生きています。
アルフェ

9

@ 00prometheusの回答(これが最良の回答です)を展開します。

無期限に待つのではなく、タイムアウトを使用する必要があるかもしれません。

以下のbash関数は、指定された検索用語が表示されるか、指定されたタイムアウトに達するまでブロックします。

タイムアウト内に文字列が見つかった場合、終了ステータスは0になります。

wait_str() {
  local file="$1"; shift
  local search_term="$1"; shift
  local wait_time="${1:-5m}"; shift # 5 minutes as default timeout

  (timeout $wait_time tail -F -n0 "$file" &) | grep -q "$search_term" && return 0

  echo "Timeout of $wait_time reached. Unable to find '$search_term' in '$file'"
  return 1
}

サーバーを起動した直後には、おそらくログファイルはまだ存在していません。その場合、文字列を検索する前に表示されるまで待つ必要があります。

wait_server() {
  echo "Waiting for server..."
  local server_log="$1"; shift
  local wait_time="$1"; shift

  wait_file "$server_log" 10 || { echo "Server log file missing: '$server_log'"; return 1; }

  wait_str "$server_log" "Server Started" "$wait_time"
}

wait_file() {
  local file="$1"; shift
  local wait_seconds="${1:-10}"; shift # 10 seconds as default timeout

  until test $((wait_seconds--)) -eq 0 -o -f "$file" ; do sleep 1; done

  ((++wait_seconds))
}

使用方法は次のとおりです。

wait_server "/var/log/server.log" 5m && \
echo -e "\n-------------------------- Server READY --------------------------\n"

それで、timeoutコマンドはどこにありますか?
アナニスト

実際、使用することtimeoutは、起動できず既に終了しているサーバーを無期限に待機しない唯一の信頼できる方法です。
gluk47

1
この答えは最高です。関数をコピーして呼び出すだけで、非常に簡単で再利用できます
フリストヴリガゾフ

6

そのため、いくつかのテストを行った後、この作業を行うための簡単な1行の方法を見つけました。grepが終了するとtail -fが終了するように見えますが、キャッチがあります。ファイルが開いたり閉じたりした場合にのみトリガーされるようです。grepが一致を見つけたときに空の文字列をファイルに追加することでこれを達成しました。

tail -f logfile |grep -m 1 "Server Started" | xargs echo "" >> logfile \;

ファイルのオープン/クローズがテールを​​トリガーしてパイプが閉じていることを認識する理由がわからないので、この動作に依存しません。しかし、今のところは機能しているようです。

終了する理由は、-Fフラグと-fフラグを見てください。


1
これは、ログファイルに追加するとtail別の行が出力されますが、それgrepまでに終了しているために機能します(おそらく、競合状態があります)。別の行を書き込むgrepまでに終了した場合tailtailを取得しSIGPIPEます。これによりtail、すぐに終了します。
リチャードハンセン

1
このアプローチの欠点:(1)競合状態がある(常にすぐに終了するとは限らない)(2)ログファイルへの書き込みアクセスが必要(3)ログファイルを変更しても問題ない(4)破損する可能性があるログファイル(5)にしか機能しないtail(6)戻りコードを簡単に取得できないため、異なる文字列の一致(「サーバー起動」と「サーバー起動失敗」)に応じて異なる動作をするように簡単に微調整できないパイプラインの中間段階の。これらの問題をすべて回避する代替アプローチがあります-私の答えをご覧ください。
リチャードハンセン

6

現在、与えられているように、tail -fここでのすべてのソリューションは、以前にログに記録された「Server Started」行をピックアップするリスクを抱えています(特定のケースでは、ログの行数とログファイルのローテーション/切り捨て)。

むしろ過剰に複雑なものよりも、ちょうど賢くを使用するtailと、bmike perlのsnippitを示しました。最も簡単な解決策はretail開始および停止条件パターンと正規表現サポートを統合したこれです。

retail -f -u "Server Started" server.log > /dev/null

これは、その文字列のtail -f最初の新しいインスタンスが表示されるまで通常のようにファイルを追跡し、終了します。(-u通常の「フォロー」モードの場合、ファイルの最後の10行の既存の行でオプションはトリガーされません。)


GNU tailcoreutilsから)を使用する場合、次の最も簡単なオプションは--pid、FIFO(名前付きパイプ)を使用することです。

mkfifo ${FIFO:=serverlog.fifo.$$}
grep -q -m 1 "Server Started" ${FIFO}  &
tail -n 0 -f server.log  --pid $! >> ${FIFO}
rm ${FIFO}

FIFOが使用されるのは、PIDを取得して渡すためにプロセスを個別に開始する必要があるためです。A FIFOはまだ原因へのタイムリーな書き込みにぶらぶらと同じ問題に苦しんでtail受信するSIGPIPEを、使用し--pidているので、オプションtail、それはそれが気付いたときに終了するgrep(従来は監視するために使用終了したライターのではなく、プロセスを読者が、taildoesnの」本当に気になります)。オプション-n 0tail、古い行が一致をトリガーしないように使用されます。


最後に、ステートフルtailを使用できます。これにより、現在のファイルオフセットが保存されるため、以降の呼び出しでは新しい行のみが表示されます(ファイルの回転も処理します)。この例では、古いFWTK retail*を使用しています。

retail "${LOGFILE:=server.log}" > /dev/null   # skip over current content
while true; do
    [ "${LOGFILE}" -nt ".${LOGFILE}.off" ] && 
       retail "${LOGFILE}" | grep -q "Server Started" && break
    sleep 2
done

*同じ名前、前のオプションとは異なるプログラムに注意してください。

CPUを占有するループではなく、ファイルのタイムスタンプを状態ファイル(.${LOGFILE}.off)と比較し、スリープします。-T必要に応じて「」を使用して状態ファイルの場所を指定します。上記では現在のディレクトリを想定しています。その条件をスキップしても構いません。Linuxでは、inotifywait代わりにより効率的な方法を使用できます。

retail "${LOGFILE:=server.log}" > /dev/null
while true; do
    inotifywait -qq "${LOGFILE}" && 
       retail "${LOGFILE}" | grep -q "Server Started" && break
done

retail「120秒が過ぎてもリテールがまだ行を読み取っていない場合は、エラーコードを入力してリテールを終了する」などのタイムアウトと組み合わせることができますか?
キルテック

@kiltekは、GNU timeout(coreutils)を使用して起動し、retailタイムアウト時に終了コード124をチェックしtimeoutます(設定した時間以降に使用するコマンドを
強制

4

プロセスの制御とシグナル伝達を行う必要があるため、これは少し注意が必要です。もっと厄介なのは、PID追跡を使用した2つのスクリプトソリューションです。より良いのは、このような名前付きパイプを使用することです。

どのシェルスクリプトを使用していますか?

迅速で汚い、1つのスクリプトソリューション-File :Tailを使用してperlスクリプトを作成します

use File::Tail;
$file=File::Tail->new(name=>$name, maxinterval=>300, adjustafter=>7);
while (defined($line=$file->read)) {
    last if $line =~ /Server started/;
}

そのため、whileループ内で印刷するのではなく、文字列の一致をフィルター処理してwhileループから抜け出し、スクリプトを続行できます。

これらのどちらにも、探している監視フロー制御を実装するための少しの学習が必要です。


bashを使用します。私のperl-fuはそれほど強力ではありませんが、これを試してみます。
アレックスホフスティード

パイプを使用する-彼らはbashを愛し、bashはそれらを愛しています。(そして、バックアップソフトウェアがパイプの1つにヒットすると、あなたを尊重します)
-bmike

maxinterval=>3005分ごとにファイルをチェックすることを意味します。私は私のラインが一時ファイルに表示されますことを知っているので、私ははるかに積極的なポーリングを使用しています:maxinterval=>0.2, adjustafter=>10000
スティーブンOstermiller

2

ファイルが表示されるのを待ちます

while [ ! -f /path/to/the.file ] 
do sleep 2; done

ファイル内で文字列が出現するのを待つ

while ! grep "the line you're searching for" /path/to/the.file  
do sleep 10; done

https://superuser.com/a/743693/129669


2
このポーリングには2つの主な欠点があります。1.ログを何度も通過することで計算時間を浪費します。/path/to/the.file1.4GBの大きさを考慮してください。これが問題であることは明らかです。2.ログエントリが表示されたときに、最悪の場合は10秒間、必要以上に待機します。
アルフェ

2

私はこれよりもきれいな解決策を想像することはできません:

#!/usr/bin/env bash
# file : untail.sh
# usage: untail.sh logfile.log "Server Started"
(echo $BASHPID; tail -f $1) | while read LINE ; do
    if [ -z $TPID ]; then
        TPID=$LINE # the first line is used to store the previous subshell PID
    else
        echo "$LINE"; [[ "$LINE" == *"${*:2}"* ]] && kill -3 $TPID && break
    fi
done

OK、名前は改善される可能性があります...

利点:

  • 特別なユーティリティは使用しません
  • ディスクに書き込まない
  • それは優雅に尾を終了し、パイプを閉じます
  • それはかなり短く、理解しやすいです

2

そのためにtailは必要ありません。watchコマンドはあなたが探しているものだと思います。watchコマンドはファイルの出力を監視し、出力が変更されたときに-gオプションで終了できます。

watch -g grep -m 1 "Server Started" logfile.log && Yournextaction

1
これは2秒に1回実行されるため、ログファイルに行が表示されてもすぐには終了しません。また、ログファイルが非常に大きい場合はうまく機能しません。
リチャードハンセン


1

アレックス私はこれがあなたを大いに助けると思う。

tail -f logfile |grep -m 1 "Server Started" | xargs echo "" >> /dev/null ;

このコマンドはログファイルにエントリを決して与えませんが、静かにgrepします...


1
これは機能しません-追加しlogfileないと、tail別の行を出力し、それgrepが(を介してSIGPIPE)死んだことを検出するまでに任意の長い時間がかかる可能性があります。
リチャードハンセン

1

ログファイルへの書き込みを必要としないはるかに優れたソリューションがありますが、これは非常に危険であるか、場合によっては不可能です。

sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'

現在、副作用は1つだけです。tailプロセスは、次の行がログに書き込まれるまでバックグラウンドのままになります。


tail -n +0 -fファイルの先頭から始まります。 tail -n 0 -fファイルの終わりから始まります。
スティーブンオステルミラー14

1
私が得る別の副作用:myscript.sh: line 14: 7845 Terminated sh -c 'tail...
スティーブンオステルミラー14

この回答では、「次のリスト」は「次の行」であると考えています。
スティーブンオステルミラー14

これは機能しますが、tailプロセスはバックグラウンドで実行されたままです。
cbaldan

1

ここの他のソリューションには、いくつかの問題があります。

  • ロギングプロセスがすでにダウンしているか、ループ中にダウンした場合、それらは無期限に実行されます
  • 表示のみが必要なログの編集
  • 不必要に追加のファイルを書き込む
  • 追加のロジックを許可しない

tomcatを例として使用して思いついたものを次に示します(開始時にログを表示する場合はハッシュを削除します)。

function startTomcat {
    loggingProcessStartCommand="${CATALINA_HOME}/bin/startup.sh"
    loggingProcessOwner="root"
    loggingProcessCommandLinePattern="${JAVA_HOME}"
    logSearchString="org.apache.catalina.startup.Catalina.start Server startup"
    logFile="${CATALINA_BASE}/log/catalina.out"

    lineNumber="$(( $(wc -l "${logFile}" | awk '{print $1}') + 1 ))"
    ${loggingProcessStartCommand}
    while [[ -z "$(sed -n "${lineNumber}p" "${logFile}" | grep "${logSearchString}")" ]]; do
        [[ -z "$(ps -ef | grep "^${loggingProcessOwner} .* ${loggingProcessCommandLinePattern}" | grep -v grep)" ]] && { echo "[ERROR] Tomcat failed to start"; return 1; }
        [[ $(wc -l "${logFile}" | awk '{print $1}') -lt ${lineNumber} ]] && continue
        #sed -n "${lineNumber}p" "${logFile}"
        let lineNumber++
    done
    #sed -n "${lineNumber}p" "${logFile}"
    echo "[INFO] Tomcat has started"
}

1

tailコマンドをバックグラウンドにすることができ、そのPIDがにエコーgrepサブシェル。でgrepサブシェルEXITのトラップハンドラを殺すことができるtailコマンドを。

( (sleep 1; exec tail -f logfile.log) & echo $! ; wait ) | 
     (trap 'kill "$pid"' EXIT; pid="$(head -1)"; grep -m 1 "Server Started")

1

それらをすべて読んでください。tldr:grepからテールの終端を切り離します。

最も便利な2つの形式は

( tail -f logfile.log & ) | grep -q "Server Started"

そして、あなたがバッシュを持っている場合

grep -m 1 "Server Started" <(tail -f logfile.log)

しかし、背景に座っているそのしっぽが気に入らなければ、ここでfifoや他の答えよりも良い方法があります。bashが必要です。

coproc grep -m 1 "Server Started"
tail -F /tmp/x --pid $COPROC_PID >&${COPROC[1]}

または、物事を出力しているのがテールでない場合、

coproc command that outputs
grep -m 1 "Sever Started" ${COPROC[0]}
kill $COPROC_PID

0

inotify(inotifywait)を使用してみてください

ファイルの変更に対してinotifywaitを設定し、grepでファイルをチェックします。見つからない場合はinotifywaitを再実行し、見つかった場合はループを終了します...そのようなSmth


この方法では、何かが書き込まれるたびにファイル全体を再チェックする必要があります。ログファイルではうまく機能しません。
悲しみ

1
別の方法は、2つのスクリプトを作成することです。1. tail -f logfile.log | grep -m 1 "Server Started"> / tmp / found 2. firstscript.sh&MYPID = $!; inotifywait -e MODIFY / tmp / found; 殺す-キル-$ MYPID
イベンガード

回答を編集して、PIDをキャプチャしてからinotifywaitを使用することを示してください。grepに慣れているが、より洗練されたツールを必要とする人にとっては簡単に理解できるエレガントなソリューションです。
bmike

あなたがキャプチャしたいもののPID?あなたが欲しいものをもう少し説明するなら、私はそれを作ろうとすることができます
イブンガード

0

行が書き込まれたらすぐに退出したいが、タイムアウト後に退出したい場合:

if (timeout 15s tail -F -n0 "stdout.log" &) | grep -q "The string that says the startup is successful" ; then
    echo "Application started with success."
else
    echo "Startup failed."
    tail stderr.log stdout.log
    exit 1
fi

-2

これはどう:

真実ながら [ -z $(grep "myRegEx" myLog.log)]; その後、壊れます。fi; やった

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.