Pregunta

Esta respuesta a Línea de comando para auto-matar a un comando después de una cierta cantidad de tiempo

propone un 1-método de la línea de tiempo de espera de un tiempo de ejecución de comandos desde la línea de comando de bash:

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

Pero es posible que dada la "larga duración" comando puede acabar antes de que el tiempo de espera.(Vamos a llamarlo un "normalmente-de larga duración-pero-a veces-rápido" de comandos, o tlrbsf para la diversión.)

Así que este ingenioso 1-forro de enfoque tiene un par de problemas.En primer lugar, la sleep no es condicional, por lo que establece un indeseable límite inferior por el tiempo en el que la secuencia final.Considerar los 30s o 2m o incluso 5m para el sueño, cuando la tlrbsf comando termina en 2 segundos — muy indeseables.Segundo, la kill es incondicional, por lo que esta secuencia de intento de matar a un no-proceso de ejecución y de quejarse de que.

Así que...

Hay una manera a tiempo de espera de un típico-de larga duración-pero-a veces-rápido ("tlrbsf" comando que

  • tiene un golpe de aplicación (la otra pregunta ya ha Perl y C respuestas)
  • terminará en la primera de las dos: tlrbsf a la finalización del programa, o el tiempo de espera transcurrido
  • no va a matar a los no-existente o no la ejecución de los procesos (o, si lo desea:no se quejan acerca de un mal matar)
  • no tiene que ser un 1-liner
  • se puede ejecutar bajo Cygwin o Linux

...y, para los puntos de bonificación, se ejecuta el tlrbsf comando en el primer plano y cualquier 'dormir' o extra proceso en segundo plano, tales que la stdin/stdout/stderr de la tlrbsf comando puede ser redirigido, el mismo que si se hubiera ejecutado directamente?

Si es así, por favor comparta su código.Si no, por favor explique por qué.

He pasado un rato tratando de hackear el ejemplo anterior, pero estoy golpeando el límite de mi bash habilidades.

¿Fue útil?

Solución

Creo que esto es precisamente lo que está pidiendo:

http://www.bashcookbook.com/bashinfo/ fuente / bash-4.0 / examples / scripts / timeout3

#!/bin/bash
#
# The Bash shell script executes a command with a time-out.
# Upon time-out expiration SIGTERM (15) is sent to the process. If the signal
# is blocked, then the subsequent SIGKILL (9) terminates it.
#
# Based on the Bash documentation example.

# Hello Chet,
# please find attached a "little easier"  :-)  to comprehend
# time-out example.  If you find it suitable, feel free to include
# anywhere: the very same logic as in the original examples/scripts, a
# little more transparent implementation to my taste.
#
# Dmitry V Golovashkin <Dmitry.Golovashkin@sas.com>

scriptName="${0##*/}"

declare -i DEFAULT_TIMEOUT=9
declare -i DEFAULT_INTERVAL=1
declare -i DEFAULT_DELAY=1

# Timeout.
declare -i timeout=DEFAULT_TIMEOUT
# Interval between checks if the process is still alive.
declare -i interval=DEFAULT_INTERVAL
# Delay between posting the SIGTERM signal and destroying the process by SIGKILL.
declare -i delay=DEFAULT_DELAY

function printUsage() {
    cat <<EOF

Synopsis
    $scriptName [-t timeout] [-i interval] [-d delay] command
    Execute a command with a time-out.
    Upon time-out expiration SIGTERM (15) is sent to the process. If SIGTERM
    signal is blocked, then the subsequent SIGKILL (9) terminates it.

    -t timeout
        Number of seconds to wait for command completion.
        Default value: $DEFAULT_TIMEOUT seconds.

    -i interval
        Interval between checks if the process is still alive.
        Positive integer, default value: $DEFAULT_INTERVAL seconds.

    -d delay
        Delay between posting the SIGTERM signal and destroying the
        process by SIGKILL. Default value: $DEFAULT_DELAY seconds.

As of today, Bash does not support floating point arithmetic (sleep does),
therefore all delay/time values must be integers.
EOF
}

# Options.
while getopts ":t:i:d:" option; do
    case "$option" in
        t) timeout=$OPTARG ;;
        i) interval=$OPTARG ;;
        d) delay=$OPTARG ;;
        *) printUsage; exit 1 ;;
    esac
done
shift $((OPTIND - 1))

# $# should be at least 1 (the command to execute), however it may be strictly
# greater than 1 if the command itself has options.
if (($# == 0 || interval <= 0)); then
    printUsage
    exit 1
fi

# kill -0 pid   Exit code indicates if a signal may be sent to $pid process.
(
    ((t = timeout))

    while ((t > 0)); do
        sleep $interval
        kill -0 $$ || exit 0
        ((t -= interval))
    done

    # Be nice, post SIGTERM first.
    # The 'exit 0' below will be executed if any preceeding command fails.
    kill -s SIGTERM $$ && kill -0 $$ || exit 0
    sleep $delay
    kill -s SIGKILL $$
) 2> /dev/null &

exec "$@"

Otros consejos

Usted probablemente está buscando el comando timeout en coreutils. Ya que es una parte de coreutils, es técnicamente una solución C, pero aún así es coreutils. info timeout para más detalles. He aquí un ejemplo:

timeout 5 /path/to/slow/command with options

Esta solución funciona independientemente del modo de monitor de fiesta. Se puede utilizar la señal adecuada a finalizar your_command

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

El vigilante mata your_command tras el tiempo límite dado; el script espera para la tarea lenta y termina el observador. Tenga en cuenta que wait no funciona con procesos que son hijos de un shell diferente.

Ejemplos:

  • your_command corre más de 2 segundos y se terminó
  

your_command interrumpido

( sleep 20 ) & pid=$!
( sleep 2 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "your_command finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "your_command interrupted"
fi
  • your_command terminado antes de que el tiempo de espera (20 segundos)
  

your_command terminado

( sleep 2 ) & pid=$!
( sleep 20 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "your_command finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "your_command interrupted"
fi

Hay que ir:

timeout --signal=SIGINT 10 /path/to/slow command with options

Puede cambiar el SIGINT y 10 como se desee;)

Yo prefiero "límite de tiempo", que tiene un paquete por lo menos en Debian.

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

Es un poco mejor que el coreutils "tiempo de espera", ya que imprime algo cuando matar el proceso, y también envía SIGKILL después de algún tiempo por defecto.

Usted puede hacer esto en su totalidad con bash 4.3 y superiores:

_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; r=$?; kill -9 `jobs -p`; exit $r; ) }
  • Ejemplo: _timeout 5 longrunning_command args
  • Ejemplo: { _timeout 5 producer || echo KABOOM $?; } | consumer
  • Ejemplo: producer | { _timeout 5 consumer1; consumer2; }
  • Ejemplo: { while date; do sleep .3; done; } | _timeout 5 cat | less

  • Necesidades Bash 4.3 para wait -n

  • 137 da si el comando fue muerto, de lo contrario el valor de retorno del comando.
  • funciona para tuberías. (No es necesario ir en primer plano aquí!)
  • funciona con comandos shell internos o funciones, también.
  • se ejecuta en una subcapa, por lo que no exportación de variables en el shell actual, lo siento.

Si usted no necesita el código de retorno, esto se puede hacer aún más sencilla:

_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; kill -9 `jobs -p`; ) }

Notas:

  • En sentido estricto no es necesario el ; en ; ), sin embargo, hace algo más consistente a la ; } caso. Y el set +b probablemente se puede dejar fuera, también, pero es mejor prevenir que curar.

  • A excepción de --forground (probablemente) se puede aplicar a todas las variantes timeout apoya. --preserve-status es un poco difícil, sin embargo. Esto se deja como ejercicio para el lector;)

Esta receta se puede utilizar "naturalmente" en la cáscara (tan natural como para flock fd):

(
set +b
sleep 20 &
{
YOUR SHELL CODE HERE
} &
wait -n
kill `jobs -p`
)

Sin embargo, como se explicó anteriormente, no se puede re-exportación de variables de entorno en el shell que encierra esta forma de manera natural.

Editar:

Ejemplo real: Tiempo de espera __git_ps1 en caso de que se tarda demasiado tiempo (para cosas como SSHFS-links lentos):

eval "__orig$(declare -f __git_ps1)" && __git_ps1() { ( git() { _timeout 0.3 /usr/bin/git "$@"; }; _timeout 0.3 __orig__git_ps1 "$@"; ) }

Edit2: Solución de error. Me di cuenta de que no es necesaria exit 137 y hace _timeout poco fiable al mismo tiempo.

Edit3: git es duro de matar, por lo que necesita un doble-trick para trabajar satisfactoriamente

.

Edit4: ¿Ha olvidado un _ en el primer _timeout para el ejemplo GIT mundo real

.

Véase también el http://www.pixelbeat.org/scripts/timeout escritura de la la funcionalidad de los cuales se ha integrado en los nuevos coreutils

Un poco hacky, pero funciona. No funciona si tiene otros procesos en primer plano (por favor ayuda a solucionar este!)

sleep TIMEOUT & SPID=${!}; (YOUR COMMAND HERE; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}

En realidad, creo que se puede revertir, que satisfacen los criterios de bonificación '':

(YOUR COMMAND HERE & SPID=${!}; (sleep TIMEOUT; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}) < asdf > fdsa

tiempo de espera es, probablemente, el primer acercamiento a tratar.Usted puede necesitar de la notificación o de otro comando a ejecutar si el tiempo de espera.Después de un poco de búsqueda y la experimentación, se me ocurrió esto bash secuencia de comandos:

if 
    timeout 20s COMMAND_YOU_WANT_TO_EXECUTE;
    timeout 20s AS_MANY_COMMANDS_AS_YOU_WANT;
then
    echo 'OK'; #if you want a positive response
else
    echo 'Not OK';
    AND_ALTERNATIVE_COMMANDS
fi

secuencia de comandos simple con la claridad del código. Guardar en /usr/local/bin/run:

#!/bin/bash

# run
# Run command with timeout $1 seconds.

# Timeout seconds
timeout_seconds="$1"
shift

# PID
pid=$$

# Start timeout
(
  sleep "$timeout_seconds"
  echo "Timed out after $timeout_seconds seconds"
  kill -- -$pid &>/dev/null
) &
timeout_pid=$!

# Run
"$@"

# Stop timeout
kill $timeout_pid &>/dev/null

Los tiempos fuera un comando que se ejecuta demasiado largo:

$ run 2 sleep 10
Timed out after 2 seconds
Terminated
$

termina inmediatamente para un comando que completa:

$ run 10 sleep 2
$

Para el tiempo de espera el slowcommand después de 1 segundo:

timeout 1 slowcommand || echo "I failed, perhaps due to time out"

Si ya conoce el nombre del programa (vamos a suponer program) para terminar después de que el tiempo de espera (como ejemplo 3 segundos), puedo aportar una solución alternativa simple y algo sucia:

(sleep 3 && killall program) & ./program

Esto funciona perfectamente si llamo procesos de referencia con las llamadas al sistema.

También hay cratimeout por Martin Cracauer (escrito en C para sistemas Unix y Linux).

# cf. http://www.cons.org/cracauer/software.html
# usage: cratimeout timeout_in_msec cmd args
cratimeout 5000 sleep 1
cratimeout 5000 sleep 600
cratimeout 5000 tail -f /dev/null
cratimeout 5000 sh -c 'while sleep 1; do date; done'

OS X no utilizar fiesta de 4, sin embargo, ni tiene / usr / bin / tiempo de espera, así que aquí tiene una función que funciona en OS X sin cerveza casera o macports que es similar a / usr / bin / tiempo de espera ( basado en la respuesta de Tino). validación de parámetros, ayuda, uso, y el apoyo a otras señales son un ejercicio para el lector.

# implement /usr/bin/timeout only if it doesn't exist
[ -n "$(type -p timeout 2>&1)" ] || function timeout { (
    set -m +b
    sleep "$1" &
    SPID=${!}
    ("${@:2}"; RETVAL=$?; kill ${SPID}; exit $RETVAL) &
    CPID=${!}
    wait %1
    SLEEPRETVAL=$?
    if [ $SLEEPRETVAL -eq 0 ] && kill ${CPID} >/dev/null 2>&1 ; then
      RETVAL=124
      # When you need to make sure it dies
      #(sleep 1; kill -9 ${CPID} >/dev/null 2>&1)&
      wait %2
    else
      wait %2
      RETVAL=$?
    fi
    return $RETVAL
) }

se me presentó un problema de preservar el contexto concha y permitir que los tiempos de espera, el único problema es que se detendrá la ejecución del script en el tiempo de espera - pero está bien con las necesidades que se presentó:

#!/usr/bin/env bash

safe_kill()
{
  ps aux | grep -v grep | grep $1 >/dev/null && kill ${2:-} $1
}

my_timeout()
{
  typeset _my_timeout _waiter_pid _return
  _my_timeout=$1
  echo "Timeout($_my_timeout) running: $*"
  shift
  (
    trap "return 0" USR1
    sleep $_my_timeout
    echo "Timeout($_my_timeout) reached for: $*"
    safe_kill $$
  ) &
  _waiter_pid=$!
  "$@" || _return=$?
  safe_kill $_waiter_pid -USR1
  echo "Timeout($_my_timeout) ran: $*"
  return ${_return:-0}
}

my_timeout 3 cd scripts
my_timeout 3 pwd
my_timeout 3 true  && echo true || echo false
my_timeout 3 false && echo true || echo false
my_timeout 3 sleep 10
my_timeout 3 pwd

con las salidas:

Timeout(3) running: 3 cd scripts
Timeout(3) ran: cd scripts
Timeout(3) running: 3 pwd
/home/mpapis/projects/rvm/rvm/scripts
Timeout(3) ran: pwd
Timeout(3) running: 3 true
Timeout(3) ran: true
true
Timeout(3) running: 3 false
Timeout(3) ran: false
false
Timeout(3) running: 3 sleep 10
Timeout(3) reached for: sleep 10
Terminated

Por supuesto que supongo que había una llamada dir scripts

#! /bin/bash
timeout=10
interval=1
delay=3
(
    ((t = timeout)) || :

    while ((t > 0)); do
        echo "$t"
        sleep $interval
        # Check if the process still exists.
        kill -0 $$ 2> /dev/null || exit 0
        ((t -= interval)) || :
    done

    # Be nice, post SIGTERM first.
    { echo SIGTERM to $$ ; kill -s TERM $$ ; sleep $delay ; kill -0 $$ 2> /dev/null && { echo SIGKILL to $$ ; kill -s KILL $$ ; } ; }
) &

exec "$@"

Mi problema era quizás un poco diferente: me pongo un comando a través de ssh en una máquina remota y quiero matar a la cáscara y del niño si el mandato se cuelga

.

Ahora utilizo el siguiente:

ssh server '( sleep 60 && kill -9 0 ) 2>/dev/null & my_command; RC=$? ; sleep 1 ; pkill -P $! ; exit $RC'

De esta manera el comando devuelve 255 cuando hubo un tiempo de espera o el código de retorno del comando en caso de éxito

Tenga en cuenta que matando a los procesos de una sesión SSH se maneja diferente de un shell interactivo. Pero también se puede utilizar la opción -t para ssh a asignar una pseudo-terminal, por lo que actúa como un intérprete de comandos interactivo

Esto es una versión que no se basa en un proceso de desove niño - que necesitaba un script independiente que incrusta esta funcionalidad. También hace un intervalo de sondeo fraccional, por lo que puede sondear más rápido. habría sido preferido de tiempo de espera - pero estoy atascado en un viejo servidor

# wait_on_command <timeout> <poll interval> command
wait_on_command()
{
    local timeout=$1; shift
    local interval=$1; shift
    $* &
    local child=$!

    loops=$(bc <<< "($timeout * (1 / $interval)) + 0.5" | sed 's/\..*//g')
    ((t = loops))
    while ((t > 0)); do
        sleep $interval
        kill -0 $child &>/dev/null || return
        ((t -= 1))
    done

    kill $child &>/dev/null || kill -0 $child &>/dev/null || return
    sleep $interval
    kill -9 $child &>/dev/null
    echo Timed out
}

slow_command()
{
    sleep 2
    echo Completed normally
}

# wait 1 sec in 0.1 sec increments
wait_on_command 1 0.1 slow_command

# or call an external command
wait_on_command 1 0.1 sleep 10

@ Loup ...

Si desea un proceso de tiempo de espera y silenciar el / salida PID trabajo matanza, ejecute:

( (sleep 1 && killall program 2>/dev/null) &) && program --version 

Esto pone el proceso en segundo plano en un subnivel por lo que no se ve la salida del trabajo.

Tengo un trabajo de cron que llama a un script php y, algunas veces, se atascan en el script php. Esta solución era perfecto para mí.

Yo uso:

scripttimeout -t 60 /script.php

En el 99% de los casos la respuesta es no implementar ninguna lógica de tiempo de espera. la lógica de tiempo de espera es en casi cualquier situación de una señal de peligro roja que algo else es un error y debe fijarse en lugar .

Es su colgante proceso o rotura después de n segundos a veces? A continuación, averiguar por qué y fijar en su lugar.

Como acotación al margen, para hacer solución correcta de Strager, es necesario utilizar espera "$ SPID" en lugar de 1 fg, ya que en las secuencias de comandos que no tiene el control de trabajos (y tratando de encenderlo es estúpido). Por otra parte, fg 1 se basa en el hecho de que no se inició ningún otro trabajo previamente en el guión, que es una suposición incorrecta de hacer.

Una forma muy simple:

# command & sleep 5; pkill -9 -x -f "command"

por pkill (opción f ) se puede matar a su comando específico con argumentos o especificar -n para evitar matar el proceso de edad.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top