ดีที่สุดคือการใช้timeout
คำสั่งถ้าคุณมีมันซึ่งมีความหมายสำหรับที่:
timeout 86400 cmd
การใช้งาน GNU ปัจจุบัน (8.23) อย่างน้อยก็ใช้งานได้โดยใช้alarm()
หรือเทียบเท่าในขณะที่รอกระบวนการลูก ดูเหมือนจะไม่ได้รับการปกป้องจากการSIGALRM
ถูกส่งระหว่างการwaitpid()
กลับมาและการtimeout
ออก (การยกเลิกการเตือนภัยอย่างมีประสิทธิภาพ) ในช่วงหน้าต่างเล็ก ๆ นั้นtimeout
อาจเขียนข้อความบน stderr (เช่นถ้าเด็กทิ้งแกน) ซึ่งจะขยายหน้าต่างการแข่งขันต่อไป (โดยไม่มีกำหนดหาก stderr เป็นไพพ์เต็มเป็นต้น)
ฉันเองสามารถอยู่กับข้อ จำกัด นั้น (ซึ่งอาจจะได้รับการแก้ไขในรุ่นอนาคต) 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
มีtimelimit
คำสั่งที่http://devel.ringlet.net/sysutils/timelimit/ (ถือกำเนิด GNU timeout
ภายในไม่กี่เดือน)
timelimit -t 86400 cmd
อันนั้นใช้alarm()
กลไกคล้ายกัน แต่ติดตั้งตัวจัดการSIGCHLD
(ไม่สนใจลูกหยุด) เพื่อตรวจจับเด็กที่กำลังจะตาย นอกจากนี้ยังยกเลิกการเตือนก่อนที่จะทำงานwaitpid()
(ที่ไม่ได้ยกเลิกการส่งมอบSIGALRM
ถ้ามันอยู่ระหว่างดำเนินการ แต่วิธีการเขียนฉันไม่เห็นว่ามันเป็นปัญหา) และฆ่าก่อนที่จะโทรwaitpid()
(ดังนั้นไม่สามารถฆ่า pid ที่นำมาใช้ใหม่ )
netpipesยังมีtimelimit
คำสั่ง อันนั้นมีมาก่อนอีกอันหนึ่งทุกสิบปีใช้วิธีการอื่น แต่ไม่สามารถทำงานได้อย่างถูกต้องสำหรับคำสั่งหยุดและคืน1
สถานะทางออกเมื่อหมดเวลา
เพื่อเป็นการตอบคำถามของคุณโดยตรงคุณสามารถทำสิ่งต่อไปนี้
if [ "$(ps -o ppid= -p "$p")" -eq "$$" ]; then
kill "$p"
fi
นั่นคือตรวจสอบว่ากระบวนการยังคงเป็นลูกของเรา อีกครั้งมีหน้าต่างการแข่งขันขนาดเล็ก (ในระหว่างps
การรับสถานะของกระบวนการและkill
ฆ่ามัน) ในระหว่างที่กระบวนการอาจตายและ pid ของมันจะถูกนำกลับมาใช้โดยกระบวนการอื่น
ด้วยเปลือกหอยบางคน ( zsh
, bash
, mksh
) คุณสามารถส่งรายละเอียดงานแทน PIDs
cmd &
sleep 86400
kill %
wait "$!" # to retrieve the exit status
สิ่งนี้ใช้ได้ผลก็ต่อเมื่อคุณวางไข่งานแบ็คกราวนด์เพียงงานเดียวเท่านั้น
หากเป็นปัญหาให้เริ่มต้นอินสแตนซ์เชลล์ใหม่:
bash -c '"$@" & sleep 86400; kill %; wait "$!"' sh cmd
ใช้งานได้เพราะเชลล์ลบงานออกจากตารางงานเมื่อเด็กกำลังจะตาย ที่นี่ไม่ควรมีหน้าต่างการแข่งขันใด ๆ ตั้งแต่เวลาที่เชลล์เรียกkill()
สัญญาณ SIGCHLD ไม่ได้รับการจัดการและ pid ไม่สามารถนำกลับมาใช้ใหม่ได้ (เนื่องจากไม่ได้รับการรอคอย) หรือถูกจัดการและ งานถูกลบออกจากตารางกระบวนการ (และkill
จะรายงานข้อผิดพลาด) bash
's kill
ที่บล็อกน้อย SIGCHLD ก่อนที่จะเข้าถึงตารางงานของตนที่จะขยาย%
และ unblocks 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
กลับกลายโดย