そのためのtimeout
コマンドがある場合は、コマンドを使用することをお勧めします。
timeout 86400 cmd
現在(8.23)のGNU実装は、少なくともalarm()
子プロセスを待機している間に使用するか、同等のものを使用して動作します。戻ることと終了SIGALRM
することの間に配信されることを保護していないようです(そのアラームを効果的にキャンセルします)。その小さなウィンドウの間に、(たとえば、子がコアをダンプした場合)stderrにメッセージを書き込むこともあります。これにより、そのレースウィンドウがさらに大きくなります(たとえば、stderrがフルパイプの場合)。waitpid()
timeout
timeout
私は個人的にその制限に耐えることができます(おそらく将来のバージョンで修正されるでしょう)。timeout
また、適切な終了ステータスを報告し、他のコーナーケース(SIGALRMが起動時にブロック/無視される、他の信号を処理するなど)を処理するのに、おそらく手でやるよりも十分に注意します。
近似として、次のように書くことができますperl
。
perl -MPOSIX -e '
$p = fork();
die "fork: $!\n" unless defined($p);
if ($p) {
$SIG{ALRM} = sub {
kill "TERM", $p;
exit 124;
};
alarm(86400);
wait;
exit (WIFSIGNALED($?) ? WTERMSIG($?)+128 : WEXITSTATUS($?))
} else {exec @ARGV}' cmd
http://devel.ringlet.net/sysutils/timelimit/にtimelimit
コマンドがあります(GNU timeout
よりも数か月前です)。
timelimit -t 86400 cmd
それはalarm()
-likeメカニズムを使用しますが、ハンドラーをインストールしSIGCHLD
(停止した子を無視)、子の死を検出します。また、実行前にアラームをキャンセルしwaitpid()
(SIGALRM
保留中だった場合は配信をキャンセルしませんが、書き込み方法が問題であることがわかりません)、呼び出す前に殺しますwaitpid()
(したがって、再利用されたpidを殺すことはできません) )。
netpipesにもtimelimit
コマンドがあります。それは他のすべてのものよりも数十年前に行われ、さらに別のアプローチを取りますが、停止したコマンドに対して適切に機能せず1
、タイムアウト時に終了ステータスを返します。
あなたの質問に対するより直接的な答えとして、あなたは次のようなことをすることができます:
if [ "$(ps -o ppid= -p "$p")" -eq "$$" ]; then
kill "$p"
fi
つまり、プロセスがまだ私たちの子であることを確認してください。繰り返しますが、プロセスが終了し、そのpidが別のプロセスによって再利用される可能性のある小さな競合ウィンドウがあります(ps
そのプロセスのステータスの取得と終了の間kill
)。
一部のシェルでは(zsh
、bash
、mksh
)は、ジョブの仕様の代わりのPIDを渡すことができます。
cmd &
sleep 86400
kill %
wait "$!" # to retrieve the exit status
これは、バックグラウンドジョブを1つだけ生成する場合にのみ機能します(そうしないと、正しいjobspecを取得できるとは限りません)。
問題がある場合は、新しいシェルインスタンスを開始するだけです。
bash -c '"$@" & sleep 86400; kill %; wait "$!"' sh cmd
これは、子が死んだときにシェルがジョブテーブルからジョブを削除するためです。ここでは、シェルがを呼び出すkill()
までに、SIGCHLDシグナルが処理されず、pidを再利用できない(待機していないため)か、処理されて、ジョブはプロセステーブルから削除されました(kill
エラーが報告されます)。bash
「S kill
それが拡大してそのジョブ表にアクセスする前に、少なくともブロックのSIGCHLDで%
と後のブロックを解除kill()
。
それを避けるために別のオプションsleep
の後でもぶらぶらプロセスはcmd
と、死亡したbash
かksh93
とのパイプを使用することですread -t
代わりにsleep
:
{
{
cmd 4>&1 >&3 3>&- &
printf '%d\n.' "$!"
} | {
read p
read -t 86400 || kill "$p"
}
} 3>&1
これにはまだ競合状態があり、コマンドの終了ステータスが失われます。またcmd
、fd 4を閉じないことも想定しています。
次のperl
ような競合のないソリューションを実装してみてください。
perl -MPOSIX -e '
$p = fork();
die "fork: $!\n" unless defined($p);
if ($p) {
$SIG{CHLD} = sub {
$ss = POSIX::SigSet->new(SIGALRM); $oss = POSIX::SigSet->new;
sigprocmask(SIG_BLOCK, $ss, $oss);
waitpid($p,WNOHANG);
exit (WIFSIGNALED($?) ? WTERMSIG($?)+128 : WEXITSTATUS($?))
unless $? == -1;
sigprocmask(SIG_UNBLOCK, $oss);
};
$SIG{ALRM} = sub {
kill "TERM", $p;
exit 124;
};
alarm(86400);
pause while 1;
} else {exec @ARGV}' cmd args...
(ただし、他のタイプのコーナーケースを処理するには改善する必要があります)。
別の競合のない方法は、プロセスグループを使用することです。
set -m
((sleep 86400; kill 0) & exec cmd)
ただし、関連する端末デバイスへのI / Oがある場合、プロセスグループの使用には副作用があることに注意してください。ただし、によって生成される他のすべての追加プロセスを強制終了することには、追加の利点がありますcmd
。