シェルスクリプトでプロセスを起動し、ユーザーと対話できるようにしたいと思います(したがって、プロセスをバックグラウンドに置いてPIDをインポートして起動し、次に完全性sleep
チェックなどを実行することはできませんkill
)。しばらく(たとえば、2分)後もまだ実行中で、その後は終了します。
目的は、この機能が組み込まれていないプログラムに対してウォールクロックランタイムタイムアウトを実装することです。
bashシェルスクリプトでこれを行う方法はありますか?
答え1
まったく新しいLinuxシステム(より正確にはGNU coreutils ≥8.5)では簡単です。
timeout 2m myprogram
POSIXシェル機能のみを使用すると近接できますが、タイムアウトが発生した場合はいくつかの競合状態があります(理論的には、2番目のバックグラウンドプロセスのPIDはkill $s
実行前に再利用できます)。
myprogram <>/dev/tty & p=$!;
{ sleep 120; kill $p; } & s=$!;
wait $p; kill $s
プログラムの出力が必要な場合でも、競合状態なしで動作する良い方法があります。これは以下を使用するきちんとしたトリックです。プロセスグループ。これはありがとう、Stephen Jimenezに行ったことがありますか?。
sh -ic '{ myprogram <>/dev/tty; kill 0; } |
{ sleep 120; kill 0; }'
答え2
BASH FAQエントリー#68:「コマンドを実行し、N秒後にコマンドを中断(タイムアウト)させる方法は何ですか?」
まず、実行中のコマンドがタイムアウトについて直接通知できることを確認してください。ここで説明されている方法は、一定時間が経過した後にコマンドを強制終了する「ハッキー」回避策です。適切に設定されたコマンドは、常に次の選択肢よりも優れています。
コマンドが指定された時間以降に停止をデフォルトでサポートしていない場合、最良の選択肢はtimeoutとdoalarmと呼ばれるいくつかの外部コマンドです。一部のLinuxディストリビューションでは、tctバージョンのタイムアウトをパッケージとして提供しています。最近のcoreutilsリリースには、GNUバージョンのタイムアウトも含まれています。
注:デフォルトでは、一部のタイムアウト実装はSIGKILL(kill -9)を実行します。これは電源コードを抜くのとほぼ同じです(プログラムがジョブを送信する機会を与えず、しばしばデータが破損する結果をもたらします)。プログラムが自分で終了できるようにする信号(SIGTERM)を使用する必要があります。 SIGKILLの詳細については、プロセス管理を参照してください。
doalarmとtimeoutの主な違いは、doalarmがアラームを設定した後にプログラムを「実行」することです。これはWrapperScriptでうまく機能しますが、timeoutはプログラムを子プロセスとして起動してから停止します(両方のプロセスが同時に存在します)。必要に応じて複数の信号を送信する機会を提供します。
上記のプログラムのいずれかが存在しない場合、または不要な場合は、Perl one-linerを使用してALRMを設定してから、制限時間内に実行したいプログラムを実行できます。それにもかかわらず、プログラムがSIGALRMで実行することを理解する必要があります。定期的に更新されるプログラムは、しばしばこの目的のためにALRMを使用し、信号が受信されたときに死ぬのではなく更新されます。
doalarm() { perl -e 'alarm shift; exec @ARGV' "$@"; } doalarm ${NUMBER_OF_SECONDS_BEFORE_ALRMING} program arg arg ...
これらのプログラムのいずれかを使用できないか使用したくない場合(実際には30年前にネイティブUnixコアユーティリティに含まれていたはずです)、最善の方法は次の醜いハッキングを実行することです。
command & pid=$! { sleep 10; kill $pid; } &
これが対話型シェルで実行されると、タイムアウト条件が有効になっているかどうかにかかわらず、かなり混乱する可能性があることがわかります。それをきれいにすることは時間を費やす価値がありません。また、前景端末(topなど)が必要なコマンドと一緒に使用することはできません。
同様のことができますが、コマンドをフォアグラウンドに保ちます。
bash -c '(sleep 10; kill $$) & exec command'
kill $$
シェルを終了しますが、execはコマンドがシェルのPIDを引き継ぎます。 bash 4で呼び出しシェルが置き換えられないように、bash -cを使用する必要があります。代わりにサブシェルを使用できます。( cmdpid=$BASHPID; (sleep 10; kill $cmdpid) & exec command )
シェルスクリプト "timeout"("timeout"コマンドと混同しないでください)は、上記の2番目の方法を使用します。すぐに動作するという利点(プログラムをコンパイルする必要はありません)がありますが、プログラムが標準入力を読み取るなど、いくつかの問題があります。
タイムアウトまたはドアアームを使用してください。本当。
答え3
Gilesが提供した解決策に「だから邪悪な行動をやめる」という内容を追加したかったのです。私のbashscriptフィルタに保存します。何かを期待していましたが、ログなしでタイムアウトするとビューには良くないので、次のように少し改善したいと思います。
timeout 1s sleep 3 || echo "sleep exceeded time"
タイムアウトが1秒を超える場合にのみログが提供され、それ以外の場合は何も提供されません。