Yang terbaik adalah menggunakan timeoutperintah 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 SIGALRMyang disampaikan di antara waitpid()kembali dan timeoutkeluar (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). timeoutjuga 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 perlseperti:
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 timelimitperintah di http://devel.ringlet.net/sysutils/timelimit/ (mendahului GNU timeoutbeberapa 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 SIGALRMjika 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 timelimitperintah. 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 psmengambil status proses itu dan killmembunuhnya) 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 killakan melaporkan kesalahan). bashIni killsetidaknya blok SIGCHLD sebelum mengakses meja tugasnya untuk memperluas %dan unblocks itu setelah kill().
Pilihan lain untuk menghindari sleepproses yang berkeliaran bahkan setelah cmdtelah mati, dengan bashatau ksh93menggunakan pipa dengan read -tbukannya 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 cmdtidak menutup fd 4.
Anda dapat mencoba menerapkan solusi bebas ras di perlseperti:
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.