Frage

Ich möchte automatisch einen Befehl nach einer gewissen Zeit töten. Ich habe eine Schnittstelle wie dies im Sinne:

% constrain 300 ./foo args

Welche „./foo“ mit ‚args‘ laufen würde, aber es automatisch zu töten, wenn es nach wie vor ist nach 5 Minuten ausgeführt wird.

Es könnte nützlich sein, um die Idee zu anderen Einschränkungen, wie autokilling ein Verfahren zu verallgemeinern, wenn es zu viel Speicher verwendet.

Gibt es irgendwelche vorhandenen Werkzeuge, die das tun, oder hat jemand geschrieben, so etwas?

ADDED: Jonathan Lösung ist genau das, was ich im Sinn hatte, und es wirkt wie ein Zauber auf Linux, aber ich kann es nicht auf Mac OSX zu arbeiten. Ich habe von der SIGRTMIN befreien, die sie zusammenstellen fein lässt, aber das Signal nicht nur für das Kind Prozess geschickt bekommt. Wer weiß, wie man diese Arbeit auf dem Mac machen?

[Added:. Beachten Sie, dass ein Update von Jonathan verfügbar ist, die auf Mac arbeitet und an anderer Stelle]

Andere Tipps

Vielleicht verstehe ich die Frage nicht, aber das klingt machbar direkt, zumindest in der Bash:

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

Dadurch wird der erste Befehl in der Klammer, für fünf Sekunden und tötet sie dann. Der gesamte Vorgang läuft synchron, das heißt Sie werden nicht in der Lage sein, Ihre Shell zu verwenden, während es beschäftigt ist, für den langsamen Befehl wartet. Wenn das nicht das, was man wollte, soll es möglich sein, eine andere & hinzuzufügen.

Die $! Variable ist ein Bash builtin, die die Prozess-ID des zuletzt gestartet Subshell enthält. Es ist wichtig, die & in der Klammer nicht zu haben, es zu tun auf diese Weise verliert den Prozess-ID.

Ich habe ein Programm timeout genannt, die das tut -. Geschrieben in C, die ursprünglich im Jahr 1989 aber regelmäßig seitdem aktualisiert


Update: dieser Code nicht auf MacOS X kompilieren, weil SIGRTMIN nicht definiert ist, und schlägt fehl, wenn auf MacOS X laufen Timeout, weil die signal() Funktion die wait() nach den Weckzeiten dort wieder heraus - was nicht das gewünschte Verhalten ist. Ich habe eine neue Version von timeout.c, die mit behandelt diese beiden Probleme (mit sigaction() statt signal()). Wie zuvor kontaktieren Sie mich für eine 10K gzipped tar-Datei mit dem Quellcode und einer manuellen Seite (siehe mein Profil).


/*
@(#)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);
}

Wenn Sie den 'offiziellen' Code für 'stderr.h' und 'stderr.c' möchten, kontaktieren Sie mich (siehe mein Profil).

Es gibt auch ulimit, die verwendet werden kann, um die Ausführungszeit zur Verfügung zu Teilprozessen zu begrenzen.

ulimit -t 10

Begrenzt den Prozess bis 10 Sekunden CPU-Zeit.

So verwenden Sie es tatsächlich einen neuen Prozess zu begrenzen, anstatt der aktuelle Prozess, können Sie möchten einen Wrapper-Skript verwenden:

#! /usr/bin/env python

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

other-Befehl kann jedes Werkzeug sein. Ich war eine Java, Python, C und Scheme-Versionen von verschiedenen Sortieralgorithmen ausgeführt wird, und Protokollierung, wie lange sie dauerte, während der Ausführungszeit auf 30 Sekunden begrenzt. Eine Cocoa-Python-Anwendung generierte die verschiedene Befehlszeilen - Argumente einschließlich -. Und zusammengestellt, die Zeiten in eine CSV-Datei, aber es war wirklich Flusen nur auf den Befehl oben angegebenen

Perl Einzeiler, nur für Kicks:

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

Diese Drucke ‚foo‘ für 10 Sekunden, dann mal aus. Ersetzen ‚10‘ mit einer beliebigen Anzahl von Sekunden, und ‚Ja foo‘ mit jedem Befehl.

Der Timeout-Befehl von Ubuntu / Debian, wenn aus den Quellen kompiliert auf dem Mac zu arbeiten. Darwin

10.4. *

http://packages.ubuntu.com/lucid/timeout

Meine Variante der Perl Einzeiler haben Sie die Exit-Status ohne mit Gabel Ausmisten () und warten () und ohne das Risiko, den falschen Prozess zu töten:

#!/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));' "$@"

Grundsätzlich ist die fork () und warten () innerhalb System () verborgen. Die SIGALRM wird an den übergeordneten Prozess zugeführt, die dann tötet sich selbst und ihre Kinder durch SIGTERM an den gesamten Prozess Gruppensenden (- $$). Im unwahrscheinlichen Fall, dass das Kind verläßt und die pid des Kindes wird, bevor die Tötung wiederverwendet () auftritt, wird dies den falschen Prozess nicht töten, weil das neue Verfahren mit dem pid alten Kind im gleichen Prozessgruppe nicht von dem übergeordneten Perl Prozess sein wird .

Als zusätzlicher Vorteil, das Script beendet auch mit dem, was wahrscheinlich der richtige Exit-Status.

Versuchen Sie so etwas wie:

# 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 $?

Es hat den Nachteil, dass, wenn Ihr Prozess pid innerhalb der Timeout wieder verwendet wird, kann es den falschen Prozess töten. Dies ist höchst unwahrscheinlich, aber Sie können 20000+ Prozesse pro Sekunde beginnen werden. Dies könnte festgelegt werden.

I "time" verwenden, die ein Paket in der Debian-Repository ist.

http://devel.ringlet.net/sysutils/timelimit/

#!/bin/sh
( some_slow_task ) & pid=$!
( sleep $TIMEOUT && kill -HUP $pid ) 2>/dev/null & watcher=$!
wait $pid 2>/dev/null && pkill -HUP -P $watcher

Der Beobachter tötet die langsame Aufgabe nach vorgegebenen Timeout; das Skript wartet auf die langsame Aufgabe und beendet die Beobachter.

Beispiele:

  • Die langsame Aufgabe mehr als 2 Sekunden laufen und wurde beendet
  

Langsam Task unterbrochen

( 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
  • Diese langsame Aufgabe, die vor dem angegebenen Timeout beendet
  

Langsam Aufgabe beendet

( 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

Eine leichte Modifikation des Perl-Einzeiler die Exit-Status richtig.

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

Im Grunde Ausgang ($? >> 8) wird den Exit-Status des subprocess weiterleiten. Ich entschied mich für nur 77 am Exit-Status für Timeout.

Wie wäre es erwarten Tool?

## run a command, aborting if timeout exceeded, e.g. 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
}

reine 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 "$@"

Gibt es nicht eine Möglichkeit, eine bestimmte Zeit mit „at“, dies zu tun zu setzen?

$ at 05:00 PM kill -9 $pid

Es scheint viel einfacher.

Wenn Sie nicht wissen, was die pid Zahl sein wird, ich nehme an, es ist ein Weg, um Skript es mit ps aux und grep zu lesen, aber nicht sicher, wie das implementieren.

$   | 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

Ihr Skript hätte den pid lesen und es eine Variable zuweisen. Ich bin nicht übermäßig qualifiziert, aber davon ausgehen, das ist machbar.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top