macos 命令行命令在一定时间后自动终止命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/601543/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Command line command to auto-kill a command after a certain amount of time
提问by dreeves
I'd like to automatically kill a command after a certain amount of time. I have in mind an interface like this:
我想在一定时间后自动终止命令。我想到了这样的界面:
% constrain 300 ./foo args
Which would run "./foo" with "args" but automatically kill it if it's still running after 5 minutes.
它将运行带有“args”的“./foo”,但如果它在 5 分钟后仍在运行,则会自动终止它。
It might be useful to generalize the idea to other constraints, such as autokilling a process if it uses too much memory.
将这个想法推广到其他约束可能会很有用,例如如果进程使用过多内存,则自动终止进程。
Are there any existing tools that do that, or has anyone written such a thing?
是否有任何现有的工具可以做到这一点,或者是否有人写过这样的东西?
ADDED: Jonathan's solution is precisely what I had in mind and it works like a charm on linux, but I can't get it to work on Mac OSX. I got rid of the SIGRTMIN which lets it compile fine, but the signal just doesn't get sent to the child process. Anyone know how to make this work on Mac?
补充:乔纳森的解决方案正是我所想的,它在 linux 上的作用就像一个魅力,但我不能让它在 Mac OSX 上工作。我摆脱了 SIGRTMIN 让它可以正常编译,但信号只是没有发送到子进程。有谁知道如何在 Mac 上进行这项工作?
[Added: Note that an update is available from Jonathan that works on Mac and elsewhere.]
[添加:请注意,Jonathan 提供了适用于 Mac 和其他地方的更新。]
采纳答案by pilcrow
I've arrived rather late to this party, but I don't see my favorite trick listed in the answers.
我参加这个聚会的时间很晚,但我没有看到答案中列出了我最喜欢的技巧。
Under *NIX, an alarm(2)
is inherited across an execve(2)
and SIGALRM is fatal by default. So, you can often simply:
在 *NIX 下, analarm(2)
跨 an 继承execve(2)
,默认情况下 SIGALRM 是致命的。因此,您通常可以简单地:
$ doalarm () { perl -e 'alarm shift; exec @ARGV' "$@"; } # define a helper function
$ doalarm 300 ./foo.sh args
or install a trivial C wrapperto do that for you.
或者安装一个简单的 C 包装器来为你做这件事。
AdvantagesOnly one PID is involved, and the mechanism is simple. You won't kill the wrong process if, for example, ./foo.sh
exited "too quickly" and its PID was re-used. You don't need several shell subprocesses working in concert, which can be done correctly but is rather race-prone.
优点只涉及一个PID,机制简单。例如,如果./foo.sh
退出“太快”并且其 PID 被重新使用,您将不会杀死错误的进程。您不需要多个 shell 子进程协同工作,这可以正确完成,但很容易发生竞争。
DisadvantagesThe time-constrained process cannot manipulate its alarm clock (e.g., alarm(2)
, ualarm(2)
, setitimer(2)
), since this would likely clear the inherited alarm. Obviously, neither can it block or ignore SIGALRM, though the same can be said of SIGINT, SIGTERM, etc. for some other approaches.
缺点时间受限的进程不能操纵它的闹钟(例如,alarm(2)
,ualarm(2)
,setitimer(2)
),因为这可能会清除继承的警报。显然,它也不能阻止或忽略 SIGALRM,尽管对于其他一些方法,SIGINT、SIGTERM 等也是如此。
Some (very old, I think) systems implement sleep(2)
in terms of alarm(2)
, and, even today, some programmers use alarm(2)
as a crude internal timeout mechanism for I/O and other operations. In my experience, however, this technique is applicable to the vast majority of processes you want to time limit.
一些(很老了,我认为)系统实现sleep(2)
的方面alarm(2)
,而且,即使今天,一些程序员使用alarm(2)
的粗内部超时机制,I / O等操作。但是,根据我的经验,此技术适用于您想要限制时间的绝大多数流程。
回答by Roger Dahl
GNU Coreutils includes the timeoutcommand, installed by default on many systems.
GNU Coreutils 包括timeout命令,默认情况下安装在许多系统上。
https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html
https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html
To watch free -m
for one minute, then kill it by sending a TERM signal:
要观看free -m
一分钟,然后通过发送 TERM 信号来终止它:
timeout 1m watch free -m
回答by unwind
Maybe I'm not understanding the question, but this sounds doable directly, at least in bash:
也许我不理解这个问题,但这听起来直接可行,至少在 bash 中:
( /path/to/slow command with options ) & sleep 5 ; kill $!
This runs the first command, inside the parenthesis, for five seconds, and then kills it. The entire operation runs synchronously, i.e. you won't be able to use your shell while it is busy waiting for the slow command. If that is not what you wanted, it should be possible to add another &.
这将运行括号内的第一个命令五秒钟,然后将其终止。整个操作是同步运行的,也就是说,当您的 shell 忙于等待慢速命令时,您将无法使用它。如果这不是您想要的,应该可以添加另一个 &。
The $!
variable is a Bash builtin that contains the process ID of the most recently started subshell. It is important to not have the & inside the parenthesis, doing it that way loses the process ID.
该$!
变量是一个 Bash 内置函数,其中包含最近启动的子 shell 的进程 ID。重要的是不要将 & 放在括号内,这样做会丢失进程 ID。
回答by Jonathan Leffler
I have a program called timeout
that does that - written in C, originally in 1989 but updated periodically since then.
我有一个程序叫做timeout
这样做 - 用 C 编写,最初是在 1989 年,但从那时起定期更新。
更新:此代码无法在 MacOS X 上编译,因为未定义 SIGRTMIN,并且在 MacOS X 上运行时无法超时,因为
signal()
signal()
那里的函数wait()
wait()
在警报超时后恢复- 这不是必需的行为。我有一个新版本timeout.c
timeout.c
可以解决这两个问题(使用sigaction()
sigaction()
而不是signal()
signal()
)。和以前一样,联系我获取带有源代码和手册页的 10K 压缩 tar 文件(请参阅我的个人资料)。/*
@(#)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);
}
If you want the 'official' code for 'stderr.h' and 'stderr.c', contact me (see my profile).
如果您想要“stderr.h”和“stderr.c”的“官方”代码,请联系我(请参阅我的个人资料)。
回答by Matthew Schinckel
There is also ulimit, which can be used to limit the execution time available to sub-processes.
还有 ulimit,可用于限制子进程可用的执行时间。
ulimit -t 10
Limits the process to 10 seconds of CPU time.
将进程限制为 10 秒的 CPU 时间。
To actually use it to limit a new process, rather than the current process, you may wish to use a wrapper script:
要实际使用它来限制新进程,而不是当前进程,您可能希望使用包装脚本:
#! /usr/bin/env python
import os
os.system("ulimit -t 10; other-command-here")
other-command can be any tool. I was running a Java, Python, C and Scheme versions of different sorting algorithms, and logging how long they took, whilst limiting execution time to 30 seconds. A Cocoa-Python application generated the various command lines - including the arguments - and collated the times into a CSV file, but it was really just fluff on top of the command provided above.
other-command 可以是任何工具。我正在运行不同排序算法的 Java、Python、C 和 Scheme 版本,并记录它们花费的时间,同时将执行时间限制为 30 秒。一个 Cocoa-Python 应用程序生成了各种命令行——包括参数——并将时间整理到一个 CSV 文件中,但它实际上只是在上面提供的命令之上。
回答by vasi
Perl one liner, just for kicks:
Perl 一个班轮,只是为了踢:
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
This prints 'foo' for ten seconds, then times out. Replace '10' with any number of seconds, and 'yes foo' with any command.
这将打印 'foo' 十秒钟,然后超时。用任意秒数替换 '10',用任意命令替换 'yes foo'。
回答by sheki
The timeout command from Ubuntu/Debian when compiled from source to work on the Mac. Darwin
从源代码编译以在 Mac 上运行时来自 Ubuntu/Debian 的超时命令。达尔文
10.4.*
10.4.*
回答by bjaspan
My variation on the perl one-liner gives you the exit status without mucking with fork() and wait() and without the risk of killing the wrong process:
我对 perl one-liner 的变体为您提供了退出状态,而无需使用 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));' "$@"
Basically the fork() and wait() are hidden inside system(). The SIGALRM is delivered to the parent process which then kills itself and its child by sending SIGTERM to the whole process group (-$$). In the unlikely event that the child exits and the child's pid gets reused before the kill() occurs, this will NOT kill the wrong process because the new process with the old child's pid will not be in the same process group of the parent perl process.
基本上 fork() 和 wait() 隐藏在 system() 中。SIGALRM 被传递给父进程,然后父进程通过将 SIGTERM 发送到整个进程组 (-$$) 来杀死它自己和它的子进程。万一子进程退出并且子进程的 pid 在 kill() 发生之前被重用,这不会杀死错误的进程,因为具有旧子进程的新进程不会在父 perl 进程的同一个进程组中.
As an added benefit, the script also exits with what is probablythe correct exit status.
作为一个额外的好处,脚本也会以可能是正确的退出状态退出。
回答by maxy
I use "timelimit", which is a package available in the debian repository.
我使用“timelimit”,这是一个在 debian 存储库中可用的包。
回答by Dmitry
#!/bin/sh
( some_slow_task ) & pid=$!
( sleep $TIMEOUT && kill -HUP $pid ) 2>/dev/null & watcher=$!
wait $pid 2>/dev/null && pkill -HUP -P $watcher
The watcher kills the slow task after given timeout; the script waits for the slow task and terminates the watcher.
观察者在给定超时后杀死慢速任务;脚本等待慢速任务并终止观察者。
Examples:
例子:
- The slow task run more than 2 sec and was terminated
- 慢任务运行超过 2 秒并被终止
Slow task interrupted
慢任务中断
( 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
- This slow task finished before the given timeout
- 这个缓慢的任务在给定的超时之前完成
Slow task finished
慢任务完成
( 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