命令行命令在一段时间后自动杀死一个命令
我想在一段时间后自动杀死一个命令。 我有一个这样的界面:
% constrain 300 ./foo args
哪个会运行带“args”的“./foo”,但如果在5分钟后仍然运行,会自动终止。
将这个想法推广到其他约束可能是有用的,例如,如果使用太多的内存,那么自动激发一个进程。
有没有现成的工具可以做,或者有人写过这样的东西?
补充:乔纳森的解决scheme正是我想到的,它的工作原理就像在Linux上的魅力,但我不能让它在Mac OSX上工作。 我摆脱了SIGRTMIN让它编译好,但信号只是不会被发送到subprocess。 任何人都知道如何使这个工作在Mac?
[补充:请注意,乔纳森可以在Mac和其他地方使用更新。]
我到这个派对来得相当晚,但是我没有看到我最喜欢的窍门。
在* NIX下, alarm(2)
通过execve(2)
inheritance,SIGALRM默认是致命的。 所以,你可以经常简单地:
$ doalarm () { perl -e 'alarm shift; exec @ARGV' "$@"; } # define a helper function $ doalarm 300 ./foo.sh args
或者安装一个简单的C包装来为你做。
优点只涉及一个PID,机制简单。 例如,如果./foo.sh
退出“太快”,并且PID被重新使用,则不会杀死错误的进程。 你不需要几个协同工作的shellsubprocess,这可以正确完成,但是比较容易。
缺点受时间限制的进程无法操作闹钟(例如alarm(2)
, ualarm(2)
, setitimer(2)
),因为这可能会清除inheritance的闹钟。 显然,它也不能阻塞或忽略SIGALRM,但对于其他一些方法,也可以说是SIGINT,SIGTERM等。
一些(我认为很老的)系统在alarm(2)
方面实现了sleep(2)
,即使在今天,一些程序员也使用alarm(2)
作为I / O和其他操作的粗略内部超时机制。 但是,根据我的经验,这种技术适用于绝大多数你想要限制的进程。
GNU Coreutils包含超时命令,在许多系统上默认安装。
https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html
要看free -m
一分钟,然后通过发送一个TERM信号杀死它:
timeout 1m watch free -m
也许我不理解这个问题,但这听起来可以直接做到,至less在bash中是这样的:
( /path/to/slow command with options ) & sleep 5 ; kill $!
这会在括号内运行第一个命令五秒钟,然后杀死它。 整个操作同步运行,即在忙于等待慢速命令时,将无法使用shell。 如果这不是你想要的,应该可以添加另一个&。
$!
variables是一个Bash内build,它包含最近启动的子shell的进程ID。 没有“括号”的内部是很重要的,这样做会丢失进程ID。
我有一个名为timeout
的程序,它是用C语言编写的,最初是在1989年,但从那以后定期更新。
更新:此代码无法在MacOS X上编译,因为SIGRTMIN未定义,并且在MacOS X上运行时无法超时,因为signal()
函数在报警超时后恢复wait()
,这不是所需的行为。 我有一个新版本的timeout.c
处理这两个问题(使用sigaction()
而不是signal()
)。 和以前一样,请联系我一个带有源代码和手册页的10K压缩tar文件(请参阅我的configuration文件)。
/* @(#)File: $RCSfile: timeout.c,v $ @(#)Version: $Revision: 4.6 $ @(#)Last changed: $Date: 2007/03/01 22:23:02 $ @(#)Purpose: Run command with timeout monitor @(#)Author: J Leffler @(#)Copyright: (C) JLSS 1989,1997,2003,2005-07 */ #define _POSIX_SOURCE /* Enable kill() in <unistd.h> on Solaris 7 */ #define _XOPEN_SOURCE 500 #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include "stderr.h" #define CHILD 0 #define FORKFAIL -1 static const char usestr[] = "[-vV] -t time [-s signal] cmd [arg ...]"; #ifndef lint /* Prevent over-aggressive optimizers from eliminating ID string */ const char jlss_id_timeout_c[] = "@(#)$Id: timeout.c,v 4.6 2007/03/01 22:23:02 jleffler Exp $"; #endif /* lint */ static void catcher(int signum) { return; } int main(int argc, char **argv) { pid_t pid; int tm_out; int kill_signal; pid_t corpse; int status; int opt; int vflag = 0; err_setarg0(argv[0]); opterr = 0; tm_out = 0; kill_signal = SIGTERM; while ((opt = getopt(argc, argv, "vVt:s:")) != -1) { switch(opt) { case 'V': err_version("TIMEOUT", &"@(#)$Revision: 4.6 $ ($Date: 2007/03/01 22:23:02 $)"[4]); break; case 's': kill_signal = atoi(optarg); if (kill_signal <= 0 || kill_signal >= SIGRTMIN) err_error("signal number must be between 1 and %d\n", SIGRTMIN - 1); break; case 't': tm_out = atoi(optarg); if (tm_out <= 0) err_error("time must be greater than zero (%s)\n", optarg); break; case 'v': vflag = 1; break; default: err_usage(usestr); break; } } if (optind >= argc || tm_out == 0) err_usage(usestr); if ((pid = fork()) == FORKFAIL) err_syserr("failed to fork\n"); else if (pid == CHILD) { execvp(argv[optind], &argv[optind]); err_syserr("failed to exec command %s\n", argv[optind]); } /* Must be parent -- wait for child to die */ if (vflag) err_remark("time %d, signal %d, child PID %u\n", tm_out, kill_signal, (unsigned)pid); signal(SIGALRM, catcher); alarm((unsigned int)tm_out); while ((corpse = wait(&status)) != pid && errno != ECHILD) { if (errno == EINTR) { /* Timed out -- kill child */ if (vflag) err_remark("timed out - send signal %d to process %d\n", (int)kill_signal, (int)pid); if (kill(pid, kill_signal) != 0) err_syserr("sending signal %d to PID %d - ", kill_signal, pid); corpse = wait(&status); break; } } alarm(0); if (vflag) { if (corpse == (pid_t) -1) err_syserr("no valid PID from waiting - "); else err_remark("child PID %u status 0x%04X\n", (unsigned)corpse, (unsigned)status); } if (corpse != pid) status = 2; /* Dunno what happened! */ else if (WIFEXITED(status)) status = WEXITSTATUS(status); else if (WIFSIGNALED(status)) status = WTERMSIG(status); else status = 2; /* Dunno what happened! */ return(status); }
如果你想'stderr.h'和'stderr.c'的'官方'代码,请联系我(见我的个人资料)。
还有ulimit,可以用来限制subprocess可用的执行时间。
ulimit -t 10
将进程限制为10秒的CPU时间。
要实际使用它来限制一个新的进程,而不是当前的进程,你可能希望使用一个包装脚本:
#! /usr/bin/env python import os os.system("ulimit -t 10; other-command-here")
其他命令可以是任何工具。 我正在运行不同sortingalgorithm的Java,Python,C和Scheme版本,并logging了他们花了多长时间,同时将执行时间限制为30秒。 Cocoa-Python应用程序生成了各种命令行(包括参数),并将时间整理成一个CSV文件,但是在上面提供的命令之上,它确实只是一团糟。
Perl的一个class轮,只是踢:
perl -e '$s = shift; $SIG{ALRM} = sub { print STDERR "Timeout!\n"; kill INT => $p }; exec(@ARGV) unless $p = fork; alarm $s; waitpid $p, 0' 10 yes foo
这将打印“foo”十秒钟,然后超时。 用任何秒数replace“10”,用任何命令replace“yes foo”。
我在perl单线程上的变化给你退出状态,而不用fork()和wait(),并且没有杀死错误进程的风险:
#!/bin/sh # Usage: timelimit.sh secs cmd [ arg ... ] exec perl -MPOSIX -e '$SIG{ALRM} = sub { print "timeout: @ARGV\n"; kill(SIGTERM, -$$); }; alarm shift; $exit = system @ARGV; exit(WIFEXITED($exit) ? WEXITSTATUS($exit) : WTERMSIG($exit));' "$@"
基本上fork()和wait()隐藏在system()中。 SIGALRM被传递给父进程,然后通过发送SIGTERM到整个进程组( – $$)来杀死自己和它的subprocess。 在发生kill()之前,孩子退出并重新使用孩子的pid的情况下,这不会杀死错误的进程,因为具有旧孩子的pid的新进程不会在父进程的父进程的同一个进程组中。
作为一个额外的好处,脚本也退出可能是正确的退出状态。
尝试像这样:
# This function is called with a timeout (in seconds) and a pid. # After the timeout expires, if the process still exists, it attempts # to kill it. function timeout() { sleep $1 # kill -0 tests whether the process exists if kill -0 $2 > /dev/null 2>&1 ; then echo "killing process $2" kill $2 > /dev/null 2>&1 else echo "process $2 already completed" fi } <your command> & cpid=$! timeout 3 $cpid wait $cpid > /dev/null 2>& exit $?
它的缺点是,如果你的进程的PID在超时时间内被重用,它可能会杀死错误的进程。 这是不太可能的,但你可能会开始每秒20000+进程。 这可能是固定的。
我使用“timelimit”,它是debian仓库中的一个包。
perl单行的一个小的修改将得到退出状态的权利。
perl -e '$s = shift; $SIG{ALRM} = sub { print STDERR "Timeout!\n"; kill INT => $p; exit 77 }; exec(@ARGV) unless $p = fork; alarm $s; waitpid $p, 0; exit ($? >> 8)' 10 yes foo
基本上,exit($?>> 8)将转发subprocess的退出状态。 我只是select77在退出状态超时。
如何使用期望工具?
## run a command, aborting if timeout exceeded, eg timed-run 20 CMD ARGS ... timed-run() { # timeout in seconds local tmout="$1" shift env CMD_TIMEOUT="$tmout" expect -f - "$@" <<"EOF" # expect script follows eval spawn -noecho $argv set timeout $env(CMD_TIMEOUT) expect { timeout { send_error "error: operation timed out\n" exit 1 } eof } EOF }
纯粹的bash:
#!/bin/bash if [[ $# < 2 ]]; then echo "Usage: $0 timeout cmd [options]" exit 1 fi TIMEOUT="$1" shift BOSSPID=$$ ( sleep $TIMEOUT kill -9 -$BOSSPID )& TIMERPID=$! trap "kill -9 $TIMERPID" EXIT eval "$@"
#!/bin/sh ( some_slow_task ) & pid=$! ( sleep $TIMEOUT && kill -HUP $pid ) 2>/dev/null & watcher=$! wait $pid 2>/dev/null && pkill -HUP -P $watcher
观察者在超时后杀死缓慢的任务; 脚本等待缓慢的任务并终止观察者。
例子:
- 缓慢的任务运行超过2秒钟,并被终止
缓慢的任务中断
( sleep 20 ) & pid=$! ( sleep 2 && kill -HUP $pid ) 2>/dev/null & watcher=$! if wait $pid 2>/dev/null; then echo "Slow task finished" pkill -HUP -P $watcher wait $watcher else echo "Slow task interrupted" fi
- 这个缓慢的任务在给定的超时之前完成
缓慢的任务完成
( sleep 2 ) & pid=$! ( sleep 20 && kill -HUP $pid ) 2>/dev/null & watcher=$! if wait $pid 2>/dev/null; then echo "Slow task finished" pkill -HUP -P $watcher wait $watcher else echo "Slow task interrupted" fi
有没有办法设置一个特定的时间与“at”来做到这一点?
$ at 05:00 PM kill -9 $pid
似乎很简单。
如果你不知道pid号码是什么,我认为有一种方法来脚本阅读ps aux和grep,但不知道如何实现。
$ | grep someprogram tony 11585 0.0 0.0 3116 720 pts/1 S+ 11:39 0:00 grep someprogram tony 22532 0.0 0.9 27344 14136 ? S Aug25 1:23 someprogram
您的脚本将不得不读取PID并为其分配一个variables。 我不是过于熟练,但认为这是可行的。