Yang terbaik adalah menggunakan timeout
perintah jika Anda memilikinya yang dimaksudkan untuk itu:
timeout 86400 cmd
Implementasi GNU saat ini (8,23) setidaknya bekerja dengan menggunakan alarm()
atau setara saat menunggu proses anak. Tampaknya tidak menjaga terhadap SIGALRM
yang disampaikan di antara waitpid()
kembali dan timeout
keluar (secara efektif membatalkan alarm itu ). Selama jendela kecil itu,timeout
bahkan mungkin menulis pesan di stderr (misalnya jika anak membuang inti) yang akan lebih memperbesar jendela ras itu (tanpa batas jika stderr adalah pipa penuh misalnya).
Saya pribadi dapat hidup dengan batasan itu (yang mungkin akan diperbaiki dalam versi yang akan datang). timeout
juga akan sangat berhati-hati untuk melaporkan status keluar yang benar, menangani kasus sudut lainnya (seperti SIGALRM diblokir / diabaikan pada saat startup, menangani sinyal lain ...) lebih baik daripada yang mungkin Anda bisa lakukan dengan tangan.
Sebagai perkiraan, Anda bisa menulis perl
seperti:
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
Ada timelimit
perintah di http://devel.ringlet.net/sysutils/timelimit/ (mendahului GNU timeout
beberapa bulan).
timelimit -t 86400 cmd
Yang itu menggunakan alarm()
mekanisme mirip-tetapi menginstal pawang pada SIGCHLD
(mengabaikan anak-anak yang berhenti) untuk mendeteksi anak sekarat. Itu juga membatalkan alarm sebelum menjalankan waitpid()
(itu tidak membatalkan pengiriman SIGALRM
jika itu tertunda, tetapi cara itu ditulis, saya tidak bisa melihatnya menjadi masalah) dan membunuh sebelum memanggil waitpid()
(jadi tidak bisa membunuh pid digunakan kembali ).
netpipes juga memiliki timelimit
perintah. Yang satu mendahului semua yang lain dalam beberapa dekade, membutuhkan pendekatan lain, tetapi tidak berfungsi dengan benar untuk perintah yang berhenti dan mengembalikan a1
status keluar setelah batas waktu.
Sebagai jawaban yang lebih langsung untuk pertanyaan Anda, Anda dapat melakukan sesuatu seperti:
if [ "$(ps -o ppid= -p "$p")" -eq "$$" ]; then
kill "$p"
fi
Yaitu, periksa apakah prosesnya masih anak-anak kita. Sekali lagi, ada jendela ras kecil (di antara ps
mengambil status proses itu dan kill
membunuhnya) selama proses itu bisa mati dan pid nya digunakan kembali oleh proses lain.
Dengan beberapa kerang ( zsh
, bash
, mksh
), Anda dapat melewati spesifikasi pekerjaan bukan PID.
cmd &
sleep 86400
kill %
wait "$!" # to retrieve the exit status
Itu hanya bekerja jika Anda menelurkan hanya satu pekerjaan latar belakang (jika tidak mendapatkan jobspec yang tepat tidak selalu mungkin andal).
Jika itu masalah, mulai saja instance shell baru:
bash -c '"$@" & sleep 86400; kill %; wait "$!"' sh cmd
Itu bekerja karena shell menghapus pekerjaan dari tabel pekerjaan pada saat anak sekarat. Di sini, seharusnya tidak ada jendela balapan karena pada saat shell memanggil kill()
, baik sinyal SIGCHLD belum ditangani dan pid tidak dapat digunakan kembali (karena belum menunggu), atau sudah ditangani dan pekerjaan telah dihapus dari tabel proses (dan kill
akan melaporkan kesalahan). bash
Ini kill
setidaknya blok SIGCHLD sebelum mengakses meja tugasnya untuk memperluas %
dan unblocks itu setelah kill()
.
Pilihan lain untuk menghindari sleep
proses yang berkeliaran bahkan setelah cmd
telah mati, dengan bash
atau ksh93
menggunakan pipa dengan read -t
bukannya sleep
:
{
{
cmd 4>&1 >&3 3>&- &
printf '%d\n.' "$!"
} | {
read p
read -t 86400 || kill "$p"
}
} 3>&1
Yang itu masih memiliki kondisi balapan, dan Anda kehilangan status keluar perintah. Ia juga menganggap cmd
tidak menutup fd 4.
Anda dapat mencoba menerapkan solusi bebas ras di perl
seperti:
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...
(Meskipun itu perlu ditingkatkan untuk menangani jenis kasus sudut lainnya).
Metode bebas ras lain bisa menggunakan grup proses:
set -m
((sleep 86400; kill 0) & exec cmd)
Namun perhatikan bahwa menggunakan grup proses dapat memiliki efek samping jika ada I / O ke perangkat terminal yang terlibat. Ini memiliki manfaat tambahan meskipun untuk membunuh semua proses ekstra lainnya yang dihasilkan oleh cmd
.