当前位置:  开发笔记 > IOS > 正文

命令行命令在一定时间后自动终止命令

如何解决《命令行命令在一定时间后自动终止命令》经验,为你挑选了5个好方法。

我想在一段时间后自动杀死一个命令.我记得这样的界面:

% constrain 300 ./foo args

哪个会用"args"运行"./foo"但如果它在5分钟后仍在运行则会自动终止它.

将该想法概括为其他约束可能是有用的,例如如果它使用过多内存则自动填充进程.

是否有任何现有的工具可以做到这一点,或者有人写过这样的东西?

补充:Jonathan的解决方案正是我的想法,它在linux上的功能就像一个魅力,但我不能让它在Mac OSX上运行.我摆脱了SIGRTMIN,它让它编译得很好,但信号不会被发送到子进程.有人知道如何在Mac上完成这项工作吗?

[补充:请注意,Jonathan可以在Mac和其他地方使用更新.]



1> Roger Dahl..:

GNU Coreutils包含timeout命令,默认安装在许多系统上.

https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html

要观察free -m一分钟,然后通过发送一个TERM信号来杀死它:

timeout 1m watch free -m


完善!由于GNU Coreutils可以通过Homebrew获得,所以只需要"brew install coreutils",然后可以使用`gtimeout`.

2> unwind..:

也许我不理解这个问题,但这听起来很直接,至少在bash中是这样:

( /path/to/slow command with options ) & sleep 5 ; kill $!

这将在括号内运行第一个命令五秒钟,然后将其杀死.整个操作同步运行,即在忙于等待慢速命令时,您将无法使用shell.如果那不是你想要的,应该可以添加另一个&.

$!变量是Bash内置函数,包含最近启动的子shell的进程ID.重要的是不要在括号内使用&,这样做会丢失进程ID.


非常有趣,但是如果进程提前终止并且PID被重用于同一用户的另一个不相关的进程,则存在竞争条件.现在我必须阅读系统PAUSE后续的答案,看看他们是否有同样的问题.

3> pilcrow..:

我来这个派对已经很晚了,但我没有看到答案中列出我最喜欢的技巧.

在*NIX下,一个alarm(2)是继承的,execve(2)并且SIGALRM默认是致命的.所以,你通常可以简单地说:

$ doalarm () { perl -e 'alarm shift; exec @ARGV' "$@"; } # define a helper function

$ doalarm 300 ./foo.sh args

或者安装一个简单的C包装器来为你做这件事.

优点只涉及一个PID,机制很简单.例如,如果./foo.sh退出"太快"并重新使用其PID ,则不会杀死错误的进程.您不需要多个shell子进程协同工作,这可以正确完成,但更容易出现竞争.

缺点的时间约束过程中不能操纵它的闹钟(例如alarm(2),ualarm(2),setitimer(2)),因为这可能会清除继承报警.显然,它也不能阻止或忽略SIGALRM,尽管对于其他一些方法也可以说SIGINT,SIGTERM等.

一些(很老的,我认为)系统实现sleep(2)alarm(2),甚至在今天,一些程序员使用alarm(2)作为I/O和其他操作的粗略内部超时机制.但是,根据我的经验,这种技术适用于您想要限时的绝大多数流程.



4> Jonathan Lef..:

我有一个叫做的程序timeout- 用C语言编写,最初是在1989年,但从那时起定期更新.


更新:此代码无法在MacOS X上编译,因为未定义SIGRTMIN,并且在MacOS X上运行时无法超时,因为其中的signal()函数wait()在警报超时后恢复- 这不是必需的行为.我有一个新版本timeout.c处理这两个问题(使用sigaction()而不是signal()).和以前一样,请联系我获取10K gzipped 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  on Solaris 7 */
#define _XOPEN_SOURCE 500

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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'的'官方'代码,请联系我(参见我的个人资料).


对于那些太害羞而无法联系到Jonathan的人,他说我欢迎主持代码,所以这里是:http://yootles.com/outbox/timeout-4.09.tgz再次感谢Jonathan提供这个代码!(同样来自他:"顺便说一下,我认为Cygwin也需要sigaction()修复.")

5> Matthew Schi..:

还有ulimit,可用于限制子进程可用的执行时间.

ulimit -t 10

将进程限制为10秒的CPU时间.

要实际使用它来限制新进程而不是当前进程,您可能希望使用包装器脚本:

#! /usr/bin/env python

import os
os.system("ulimit -t 10; other-command-here")

other-command可以是任何工具.我正在运行不同排序算法的Java,Python,C和Scheme版本,并记录它们花了多长时间,同时将执行时间限制为30秒.Cocoa-Python应用程序生成了各种命令行 - 包括参数 - 并将时间整理成CSV文件,但它实际上只是在上面提供的命令之上.

推荐阅读
手机用户2402851155
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有