usar trucos todos son poner para los letras cuáles como comandos comando bloque macos unix command-line utilities cpu-usage

macos - trucos - Comando de línea de comando para auto-matar un comando después de una cierta cantidad de tiempo



poner letras en minecraft (15)

Me gustaría eliminar automáticamente un comando después de un cierto período de tiempo. Tengo en mente una interfaz como esta:

% constrain 300 ./foo args

Que ejecutaría "./foo" con "args" pero lo mataría automáticamente si aún se ejecuta después de 5 minutos.

Podría ser útil generalizar la idea a otras restricciones, como eliminar automáticamente un proceso si usa demasiada memoria.

¿Hay alguna herramienta existente que haga eso o alguien ha escrito algo así?

AGREGADO: La solución de Jonathan es precisamente lo que tenía en mente y funciona como un encanto en Linux, pero no puedo hacer que funcione en Mac OSX. Me deshice del SIGRTMIN que permite compilar bien, pero la señal simplemente no se envía al proceso hijo. ¿Alguien sabe cómo hacer que esto funcione en Mac?

[Agregado: tenga en cuenta que hay una actualización disponible de Jonathan que funciona en Mac y en otros lugares].


¿No hay una manera de establecer un tiempo específico con "at" para hacer esto?

$ at 05:00 PM kill -9 $pid

Parece mucho más simple.

Si no sabes cuál será el número pid, supongo que hay una manera de guionar su lectura con ps aux y grep, pero no estoy seguro de cómo implementarlo.

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

Tu script debería leer el pid y asignarle una variable. No soy demasiado habilidoso, pero supongo que esto es factible.


¿Qué hay de usar la herramienta esperar?

## 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 }




Llegué bastante tarde a esta fiesta, pero no veo mi truco favorito en las respuestas.

En * NIX, una alarm(2) se hereda en un execve(2) y SIGALRM es fatal por defecto. Entonces, a menudo puedes simplemente:

$ doalarm () { perl -e ''alarm shift; exec @ARGV'' "$@"; } # define a helper function $ doalarm 300 ./foo.sh args

o instala un envoltorio de C trivial para hacer eso por ti.

Ventajas Solo está involucrado un PID, y el mecanismo es simple. No eliminará el proceso incorrecto si, por ejemplo, ./foo.sh salió "demasiado rápido" y su PID fue reutilizado. No necesita varios subprocesos de shell que funcionen en concierto, lo que se puede hacer correctamente, pero es más bien propenso a las carreras.

Desventajas El proceso de tiempo limitado no puede manipular su reloj de alarma (por ejemplo, alarm(2) , ualarm(2) , setitimer(2) ), ya que es probable que borre la alarma heredada. Obviamente, tampoco puede bloquear o ignorar SIGALRM, aunque lo mismo puede decirse de SIGINT, SIGTERM, etc. para algunos otros enfoques.

Algunos sistemas (muy antiguos, creo) implementan el modo sleep(2) en términos de alarm(2) , e incluso hoy en día, algunos programadores usan la alarm(2) como mecanismo de tiempo de espera interno bruto para E / S y otras operaciones. En mi experiencia, sin embargo, esta técnica es aplicable a la gran mayoría de los procesos que desea limitar el tiempo.


Mi variación en el perl-liner de Perl le da el estado de salida sin tener que pasar por el tenedor () y esperar () y sin el riesgo de matar el proceso incorrecto:

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

Básicamente, la horquilla () y la espera () están ocultas dentro del sistema (). El SIGALRM se entrega al proceso padre que luego se mata a sí mismo y a su hijo al enviar SIGTERM a todo el grupo de procesos (- $$). En el caso improbable de que el niño salga y el pid del niño se reutilice antes de que kill () ocurra, esto NO matará el proceso incorrecto porque el nuevo proceso con pid del viejo no estará en el mismo grupo de procesos del proceso padre perl .

Como beneficio adicional, la secuencia de comandos también sale con lo que probablemente sea el estado de salida correcto.


Perl one liner, solo para patadas:

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

Esto imprime ''foo'' durante diez segundos y luego se agota. Reemplace ''10'' con cualquier número de segundos, y ''yes foo'' con cualquier comando.


Pruebe algo como:

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

Tiene la desventaja de que si el proceso ''pid se reutiliza dentro del tiempo de espera, puede matar el proceso incorrecto. Esto es muy poco probable, pero puede estar iniciando más de 200 procesos por segundo. Esto podría ser arreglado.


Tal vez no estoy entendiendo la pregunta, pero esto parece factible directamente, al menos en bash:

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

Esto ejecuta el primer comando, dentro del paréntesis, durante cinco segundos, y luego lo mata. Toda la operación se ejecuta de forma síncrona, es decir, no podrá usar su caparazón mientras está ocupado esperando el comando lento. Si eso no es lo que quería, debería ser posible agregar otro &.

¡Los $! variable es una compilación interna de Bash que contiene la identificación del proceso de la subescola iniciada más recientemente. Es importante no tener el & dentro del paréntesis, haciéndolo de esa manera pierde la identificación del proceso.


También hay ulimit, que se puede usar para limitar el tiempo de ejecución disponible para los subprocesos.

ulimit -t 10

Limita el proceso a 10 segundos de tiempo de CPU.

Para usarlo realmente para limitar un nuevo proceso, en lugar del proceso actual, es posible que desee utilizar un script de envoltura:

#! /usr/bin/env python import os os.system("ulimit -t 10; other-command-here")

otro-comando puede ser cualquier herramienta. Estaba ejecutando versiones en Java, Python, C y Scheme de diferentes algoritmos de clasificación, y registrando cuánto tiempo tomaron, mientras que limitaba el tiempo de ejecución a 30 segundos. Una aplicación Cocoa-Python generó varias líneas de comandos, incluidos los argumentos, y clasificó los tiempos en un archivo CSV, pero en realidad era solo pelusa en la parte superior del comando proporcionado anteriormente.


Tengo un programa llamado timeout que lo hace, escrito en C, originalmente en 1989 pero actualizado periódicamente desde entonces.

Actualización: este código no se puede compilar en MacOS X porque SIGRTMIN no está definido y no supera el tiempo de espera cuando se ejecuta en MacOS X porque la función de signal() reanuda la wait() después de que la alarma expira, lo que no es el comportamiento requerido. Tengo una nueva versión de timeout.c que trata estos dos problemas (usando sigaction() lugar de signal() ). Como antes, contáctame para obtener un archivo tar 10k con gzip con el código fuente y una página de manual (mira mi perfil).

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

Si quiere el código ''oficial'' para ''stderr.h'' y ''stderr.c'', contácteme (vea mi perfil).


Una ligera modificación del perl one-liner obtendrá el estado de salida correcto.

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

Básicamente, exit ($? >> 8) reenviará el estado de salida del subproceso. Acabo de elegir 77 en el estado de salida para el tiempo de espera.



puro 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

El observador mata a la tarea lenta después de un tiempo de espera dado; el script espera la tarea lenta y termina el observador.

Ejemplos:

  • La tarea lenta se ejecuta más de 2 segundos y finalizó

Tarea lenta interrumpida

( 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

  • Esta tarea lenta terminó antes del tiempo de espera dado

Tarea lenta terminada

( 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