Tốt nhất là sử dụng timeout
lệnh nếu bạn có nó có nghĩa là:
timeout 86400 cmd
Việc triển khai GNU (8.23) hiện tại ít nhất hoạt động bằng cách sử dụng alarm()
hoặc tương đương trong khi chờ tiến trình con. Nó dường như không bảo vệ chống lại việc SIGALRM
được giao ở giữa waitpid()
trở về và timeout
thoát (có hiệu quả hủy bỏ báo động đó ). Trong cửa sổ nhỏ đó, timeout
thậm chí có thể viết tin nhắn trên stderr (ví dụ nếu đứa trẻ đổ lõi) sẽ mở rộng thêm cửa sổ cuộc đua đó (vô thời hạn nếu stderr là một ống đầy đủ).
Cá nhân tôi có thể sống với giới hạn đó (có thể sẽ được sửa trong phiên bản tương lai). timeout
cũng sẽ cẩn thận hơn để báo cáo trạng thái thoát chính xác, xử lý các trường hợp góc khác (như SIGALRM bị chặn / bỏ qua khi khởi động, xử lý các tín hiệu khác ...) tốt hơn bạn có thể xử lý bằng tay.
Là một xấp xỉ, bạn có thể viết nó perl
như sau:
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
Có một timelimit
lệnh tại http://devel.ringlet.net/sysutils/tim006it/ (trước GNU timeout
vài tháng).
timelimit -t 86400 cmd
Cái đó sử dụng một alarm()
cơ chế giống như nhưng cài đặt một trình xử lý SIGCHLD
(bỏ qua những đứa trẻ đã dừng lại) để phát hiện đứa trẻ sắp chết. Nó cũng hủy báo thức trước khi chạy waitpid()
(không hủy giao hàng SIGALRM
nếu nó đang chờ xử lý, nhưng theo cách viết, tôi không thể thấy đó là sự cố) và giết chết trước khi gọi waitpid()
(vì vậy không thể giết chết một pid được sử dụng lại ).
netpipes cũng có một timelimit
lệnh. Cái đó có trước tất cả những cái khác trong nhiều thập kỷ, có một cách tiếp cận khác, nhưng không hoạt động đúng với các lệnh đã dừng và trả về 1
trạng thái thoát khi hết thời gian.
Là một câu trả lời trực tiếp hơn cho câu hỏi của bạn, bạn có thể làm một cái gì đó như:
if [ "$(ps -o ppid= -p "$p")" -eq "$$" ]; then
kill "$p"
fi
Đó là, kiểm tra xem quá trình này vẫn là con của chúng ta. Một lần nữa, có một cửa sổ chủng tộc nhỏ (ở giữa ps
lấy trạng thái của quá trình đó và kill
giết nó) trong quá trình đó có thể chết và pid của nó được sử dụng lại bởi một quy trình khác.
Với một số vỏ ( zsh
, bash
, mksh
), bạn có thể vượt qua thông số kỹ thuật công việc thay vì PID.
cmd &
sleep 86400
kill %
wait "$!" # to retrieve the exit status
Điều đó chỉ hoạt động nếu bạn sinh ra chỉ một công việc nền (nếu không thì việc làm đúng công việc không phải lúc nào cũng đáng tin cậy).
Nếu đó là một vấn đề, chỉ cần bắt đầu một phiên bản shell mới:
bash -c '"$@" & sleep 86400; kill %; wait "$!"' sh cmd
Điều đó hoạt động vì vỏ loại bỏ công việc khỏi bảng công việc khi đứa trẻ chết. Ở đây, không nên có bất kỳ cửa sổ cuộc đua nào kể từ khi shell gọi kill()
, tín hiệu SIGCHLD chưa được xử lý và pid không thể được sử dụng lại (vì nó chưa được chờ) hoặc đã được xử lý và công việc đã bị xóa khỏi bảng quy trình (và kill
sẽ báo lỗi). bash
là kill
ít nhất khối SIGCHLD trước khi nó truy cập vào bảng công việc của mình để mở rộng %
và unblocks nó sau kill()
.
Một lựa chọn khác để tránh sleep
quá trình đó bị treo ngay cả sau khi cmd
đã chết, có bash
hoặc ksh93
sử dụng một đường ống read -t
thay vì sleep
:
{
{
cmd 4>&1 >&3 3>&- &
printf '%d\n.' "$!"
} | {
read p
read -t 86400 || kill "$p"
}
} 3>&1
Cái đó vẫn có điều kiện chủng tộc và bạn mất trạng thái thoát lệnh. Nó cũng giả sử cmd
không đóng fd 4 của nó.
Bạn có thể thử thực hiện một giải pháp không có chủng tộc perl
như:
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...
(mặc dù nó sẽ cần phải được cải thiện để xử lý các loại trường hợp góc khác).
Một phương pháp không có chủng tộc khác có thể sử dụng các nhóm quy trình:
set -m
((sleep 86400; kill 0) & exec cmd)
Tuy nhiên, lưu ý rằng việc sử dụng các nhóm quy trình có thể có tác dụng phụ nếu có I / O cho thiết bị đầu cuối có liên quan. Nó có lợi ích bổ sung mặc dù để tiêu diệt tất cả các quá trình bổ sung khác được sinh ra bởi cmd
.