シェルスクリプトから特定の行を出力した後、プログラムを強制終了します。


15

バックグラウンド:

計算生物学ソフトウェアのテストスクリプトを書いています。私がテストしているソフトウェアは、実行するのに数日から数週間かかることもあるため、システムのクラッシュや停電の場合に備えて、回復機能が組み込まれています。

回復システムをテストする方法を見つけようとしています。具体的には、制御された方法でプログラムを「クラッシュ」させる方法がわかりません。SIGKILL命令をなんらかのタイミングで実行してからしばらくしてから実行することを考えていました。これはおそらく理想的ではありません。テストケースは毎回同じ速度で実行されることが保証されていないため(共有環境で実行されるため)、ログを目的の出力と比較するのは困難です。

このソフトウェアは、完了した分析の各セクションの行を印刷します。

質問:

プログラムからの出力をキャプチャし、特定の行/行数がプログラムによって出力されたときにプログラムを強制終了するための良い/エレガントな方法(シェルスクリプトで)があるかどうか疑問に思っていましたか?


1
特定の時間にプログラムを停止する必要があるのはなぜですか?出力のバッファリングにより、チェック対象のテキストを出力した後、プログラムが長時間終了する可能性があります。
stardt

@stardt特定の時間にプログラムを停止して、テスト結果をより制御し、再現できるようにしたかった。これを回避する方法を見つけました。プログラムを手動で1回だけ強制終了してから、回復コードのみをテストします。すべての回復試行は同じポイントから開始されます。私はそれが結局どのようにクラッシュするかではなく、どのように回復するかをテストしています:)
Paul

回答:


7

このようなラッパースクリプトは、標準的なアプローチです。スクリプトはプログラムをバックグラウンドで実行してからループし、毎分ログファイルの文字列をチェックします。文字列が見つかった場合、バックグラウンドプログラムは強制終了され、スクリプトは終了します。

command="prog -foo -whatever"
log="prog.log"
match="this is the what i want to match"

$command > "$log" 2>&1 &
pid=$!

while sleep 60
do
    if fgrep --quiet "$match" "$log"
    then
        kill $pid
        exit 0
    fi
done

14

プログラムがSIGPIPE(デフォルトのアクション)で終了する場合、その行の読み取り時に終了するリーダーに出力をパイプするだけで十分です。

だからそれは次のように簡単かもしれません

$ program | sed -e '/Suitable text from the line/q'

デフォルトの出力を抑制したい場合

$ program | sed -n -e '/Suitable text from the line/q'

同様に、特定の行数後に停止したい場合は、sedの代わりにheadを使用できます。たとえば、

$ program | head -n$NUMBER_OF_LINES_TO_STOP_AFTER

キルが発生するの正確な時間はありません stardtはコメントで示唆するように、端末のバッファリング動作に依存します。


-1。ここには重大な欠陥があります。プログラムは、終了後に(壊れた)パイプに書き込もうとした場合にのみsed終了します。OPは、「このソフトウェアは、完了した分析の各セクションの行を印刷します」と述べています。プログラムが1つの追加セクションを完了することを意味する場合があります。概念実証:{ echo 1; sleep 3; >&2 echo peekaboo; sleep 3; echo 2; } | sed -e '/1/q'この回答をご覧ください。考えられる回避策:( program & ) | sed -e '/Suitable text from the line/q'; killall program。あなたの答えを欠陥に気づかせてください。そうすれば、私は下票を取り消します。
カミルマシオロウスキ

うーん...確かに。また、バッファリングの問題により、改善しても信頼性が低くなります(もちろん、ここにあるすべてのソリューションに影響します)。可能な場合は信号インフラストラクチャを利用したいのですが、ある時点で優雅さが失われ始めます。おそらく、すべてを廃棄する方が良いでしょう。
dmckee ---元モデレーター子猫

7

dmckeeの答えに代わるものとして、オプション付きのgrepコマンド-m(たとえば、このmanページを参照)コマンドも使用できます。

compbio | grep -m 1 "Text to match"

テキストに一致する1行が見つかったときに停止するか、

compbio | grep -v -m 10 "Text to match"

指定されたテキストと一致しない10行を待機します。


3

を使用expect(1)して、プログラムの標準出力を監視し、いくつかのパターンで事前定義されたアクションを実行できます。

#!/usr/bin/env expect

spawn your-program -foo -bar 
expect "I want this text" { close }

または、別のスクリプトに埋め込むためのワンライナー:

expect -c "spawn your-program -foo -bar; expect \"I want this text\" { close }"

expectコマンドはデフォルトですべてのOSに付属しているわけではないことに注意してください。あなたはそれをインストールする必要があるかもしれません。


これは素晴らしい解決策です。set timeout -1無期限のタイムアウトを設定するように言及することをお勧めしexpectます。そうしないと、デフォルトの10秒のタイムアウトですべてクローズします。
pepper_chico

1

「while」ステートメントを使用できます。

ソフトウェア(またはそのログ)の出力をパイプし、新しい行ごとに条件付きテストを行ってから、kill -KILLを実行する必要があります。たとえば(ログファイルとして/ var / log / messagesを使用してテストしました):

tail -f /var/log/messages | while read line; do test "$line" = "THE LINE YOU WANT TO MATCH" && kill PIDTOKILL; done

または、直接ソフトウェアで:

./biosoftware | while read line; do test "$line" = "THE LINE YOU WANT TO MATCH" && kill PIDTOKILL; done

ログパス/アプリケーション名、一致する行、および強制終了するPIDを置き換える必要があります(killallを使用することもできます)。

ソフトウェアで頑張ってください、

ヒューゴ、

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