Tiempo de espera de un comando en bash sin demoras innecesarias

283

Esta respuesta al comando de línea de comando para matar automáticamente un comando después de un cierto período de tiempo

propone un método de 1 línea para agotar el tiempo de espera de un comando de ejecución larga desde la línea de comando bash:

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

Pero es posible que un comando de "ejecución prolongada" finalice antes del tiempo de espera. (Llamémoslo un comando "típicamente de larga ejecución pero a veces rápido", o tlrbsf por diversión).

Entonces, este ingenioso enfoque de 1 línea tiene un par de problemas. Primero, el sleepno es condicional, por lo que establece un límite inferior indeseable en el tiempo que tarda la secuencia en terminar. Considere 30s o 2m o incluso 5m para dormir, cuando el comando tlrbsf termine en 2 segundos, muy indeseable. En segundo lugar, el killes incondicional, por lo que esta secuencia intentará matar un proceso que no se ejecuta y quejarse al respecto.

Entonces...

¿Hay alguna manera de agotar el tiempo de espera de un comando que suele ser de larga duración pero a veces rápido ( "tlrbsf" ) que

  • tiene una implementación bash (la otra pregunta ya tiene respuestas de Perl y C)
  • terminará en el primero de los dos: tlrbsf programa o tiempo de espera transcurrido
  • no matará procesos no existentes / no en ejecución (u, opcionalmente: no se quejará de una mala eliminación)
  • no tiene que ser un trazador de líneas
  • puede ejecutarse bajo Cygwin o Linux

... y, para obtener puntos de bonificación, ejecuta el comando tlrbsf en primer plano y cualquier 'suspensión' o proceso adicional en segundo plano, de modo que el stdin / stdout / stderr del tlrbsf comando se pueda redirigir, como si hubiera sido correr directamente?

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

He pasado un tiempo tratando de hackear el ejemplo antes mencionado, pero estoy llegando al límite de mis habilidades de bash.

PAUSA del sistema
fuente
55
Otra pregunta similar: stackoverflow.com/questions/526782/… (pero creo que la respuesta 'timeout3' aquí es mucho mejor).
PAUSE del sistema
2
¿Alguna razón para no usar la timeoututilidad gnu ?
Chris Johnson
timeout¡es genial! incluso puede usar con múltiples comandos (secuencia de comandos de varias líneas): stackoverflow.com/a/61888916/658497
Noam Manos

Respuestas:

149

Creo que esto es precisamente lo que estás pidiendo:

http://www.bashcookbook.com/bashinfo/source/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 <[email protected]>

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 "$@"
Juliano
fuente
Es un buen truco, usar $$ para la parte de fondo. Y bueno que matará a un tlrbsf colgado inmediatamente. Pero ugh, tienes que elegir un intervalo de sondeo. Y si configura el sondeo demasiado bajo, consumirá CPU con señalización constante, ¡haciendo que el tlrbsf funcione aún más!
PAUSE del sistema
77
No tiene que elegir el intervalo de sondeo, tiene un valor predeterminado de 1s, eso es bastante bueno. Y la verificación es muy económica, los gastos generales son insignificantes. Dudo que eso haga que tlrbsf se ejecute notablemente más tiempo. Probé con sleep 30, y obtuve una diferencia de 0.000ms entre usarlo y no usarlo.
Juliano
66
Bien, ahora veo eso. Y cumple con mis requisitos exactos si configura el intervalo de encuesta == tiempo de espera. También funciona en canalizaciones, funciona con todo el fondo, funciona con múltiples instancias y otros trabajos en ejecución. Dulce gracias
PAUSE del sistema
El envío de una señal mata el subcarcasa, así que pensé que alinear todos los comandos de matar en una línea los preservará. También habilité la salida stderr para mostrar errores inesperados. stackoverflow.com/questions/687948/…
eel ghEEz
1
@Juliano Esa es una excelente manera de manejar los tiempos de espera, muy útil. Me pregunto si hay una manera en la que podamos hacer que el script devuelva el código de salida 143 cuando el proceso finaliza después del tiempo de espera. Intenté agregar "exit 143" justo después del comando kill, pero siempre obtengo el código de salida 0 en el script de llamada.
Salman A. Kagzi
528

Probablemente esté buscando el timeoutcomando en coreutils. Como es parte de coreutils, técnicamente es una solución C, pero sigue siendo coreutils. info timeoutpara más detalles. Aquí hay un ejemplo:

timeout 5 /path/to/slow/command with options
yingted
fuente
21
En Mac puedes instalar esto a través de Macports o homebrew.
Ivan Z. Siu
23
Cuando se instala a través de homebrew en OS X se convierte en el comandogtimeout
ethicalhack3r
55
... ¿qué sistema operativo está utilizando que tiene coreutils anteriores a 2003?
Keith el
55
@Keith: CentOS 5.10, por ejemplo :-(
Pausado hasta nuevo aviso.
9
Para aclarar, el comando homebrew que necesita en OSX / mac es brew install coreutils, y luego puede usar gtimeout.
Ohad Schneider
37

Esta solución funciona independientemente del modo de monitor bash. Puede usar la señal adecuada para finalizar su_comando

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

El observador mata your_command después de un tiempo de espera determinado; el script espera la tarea lenta y finaliza el observador. Tenga en cuenta quewait no funciona con procesos que son hijos de un shell diferente.

Ejemplos:

  • your_command se ejecuta más de 2 segundos y se finalizó

su_comando 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 finalizó antes del tiempo de espera (20 segundos)

su_comando ha 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
Dmitry
fuente
1
waitdevuelve el estado de salida del proceso en el que espera. Entonces, si su comando sale dentro del tiempo asignado pero con un estado de salida distinto de cero, entonces la lógica aquí se comportará a medida que se agota el tiempo de espera, es decir, la impresión your_command interrupted. En su lugar, podría hacer el waitsin un ify luego verificar si el $watcherpid todavía existe, si es así, entonces sabe que no se agotó el tiempo de espera.
George Hawkins
No puedo entender por qué esto se usa killen un caso, pero pkillen el otro. Necesitaba usar pkillambos para que esto funcione correctamente. Esperaría que si envuelve un comando (), deberá usarlo pkillpara matarlo. Pero tal vez funciona de manera diferente si solo hay un comando dentro del ().
nobar
Dios lo bendiga señor :)
Darko Miletic
22

Ahí tienes:

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

puede cambiar el SIGINTy 10como desee;)

Aarian P. Aleahmad
fuente
3
"timeout" es parte del paquete coreutils en (al menos) Redhat, Centos, Suse y Ubuntu, por lo que deberá instalarlo si no lo tiene.
Akom
¡es realmente útil! ¿Sabes por qué el "tiempo de espera 5 / ruta / a / lento / comando con opciones" de yingted a veces no funciona?
Decula
Desafortunadamente esto no está en el coreutilspaquete en FreeBSD.
Paul Bissex
18

Puede hacer esto completamente con bash 4.3y por encima:

_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

  • Necesita Bash 4.3 para wait -n

  • Da 137 si el comando fue asesinado, de lo contrario el valor de retorno del comando.
  • Trabajos para tuberías. (¡No necesitas ir al primer plano aquí!)
  • También funciona con comandos o funciones de shell internos.
  • Se ejecuta en un subshell, por lo que no hay exportación de variables al shell actual, lo siento.

Si no necesita el código de retorno, esto puede simplificarse aún más:

_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 ; }-case. Y set +bprobablemente también se pueda dejar de lado, pero es mejor prevenir que curar.

  • Excepto --forground(probablemente) puede implementar todos los timeoutsoportes de variantes . --preserve-statusSin embargo, es un poco difícil. Esto se deja como un ejercicio para el lector;)

Esta receta se puede usar "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 puede reexportar las variables de entorno al shell envolvente de esta manera de forma natural.

Editar:

Ejemplo del mundo real: Tiempo de espera __git_ps1en caso de que tarde demasiado (para cosas como enlaces SSHFS lentos):

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

Edit2: Bugfix. Me di cuenta de que exit 137no es necesario y hace_timeout no confiable al mismo tiempo.

Edit3: git es un disco duro, por lo que necesita un doble truco para funcionar satisfactoriamente.

Edit4: Olvidé un _en el primero _timeoutpara el ejemplo GIT del mundo real.

Tino
fuente
1
Bash 4 rocas. Eso es todo.
sistema PAUSE
1
Esto realmente requiere Bash 4.3 o más reciente. cc. The 'wait' builtin has a new '-n' option to wait for the next child to change status. De: tiswww.case.edu/php/chet/bash/NEWS
Ben Reser
17

Prefiero "timelimit", que tiene un paquete al menos en debian.

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

Es un poco mejor que el "tiempo de espera" de coreutils porque imprime algo al finalizar el proceso, y también envía SIGKILL después de un tiempo por defecto.

maxy
fuente
No parece funcionar bastante bien: / $ time timelimit -T2 sleep 10 real 0m10.003s usuario 0m0.000s sys 0m0.000s
hithwen
3
Use -t2 no -T2. El gran -T es el tiempo desde el envío de SIGTERM hasta el envío de SIGKILL.
maxy
1
Me gustaría agregar que timelimit 1.8 no parece funcionar bien con fork ( timelimit -t1 ./a_forking_progmatar solo uno de los dos procesos), pero el tiempo de espera funciona.
Jeremy Cochoy
Si desea que se agote el tiempo de espera para imprimir algo al finalizar el proceso, simplemente use la bandera "-v".
10

Para agotar el tiempo de espera slowcommanddespués de 1 segundo:

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

Para determinar si el comando agotó el tiempo de espera o falló por sus propios motivos, verifique si el código de estado es 124:

# ping for 3 seconds, but timeout after only 1 second
timeout 1 ping 8.8.8.8 -w3
EXIT_STATUS=$?
if [ $EXIT_STATUS -eq 124 ]
then
echo 'Process Timed Out!'
else
echo 'Process did not timeout. Something else went wrong.'
fi
exit $EXIT_STATUS

Tenga en cuenta que cuando el estado de salida es 124, no sabe si se agotó el tiempo de espera debido a su timeoutcomando, o si el comando en sí terminó debido a alguna lógica interna de tiempo de espera propio y luego devolvió 124. Puede asumir con seguridad en cualquier caso , sin embargo, que ocurrió un tiempo de espera de algún tipo.

lanza.dolan
fuente
9

Consulte también el script http://www.pixelbeat.org/scripts/timeout cuya funcionalidad se ha integrado en los nuevos coreutils

pixelbeat
fuente
Neat, simple, usa TERM y no KILL. ¡Agradable! Había estado explorando una solución trampa / espera como esta cuando originalmente planteé la pregunta.
PAUSE del sistema
timeout devuelve 124 en caso de una muerte.
ceving
8

Un poco hacky, pero funciona. No funciona si tienes otros procesos en primer plano (¡por favor, ayúdame a solucionar esto!)

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

En realidad, creo que puedes revertirlo, cumpliendo con tus criterios de 'bonificación':

(YOUR COMMAND HERE & SPID=${!}; (sleep TIMEOUT; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}) < asdf > fdsa
extraño
fuente
(ls -ltR / cygdrive / c / windows & SPID = $ {!}; (sleep 1s; kill $ {SPID}) & CPID = $ {!}; fg 1; kill $ {CPID})> fdsa
pausa del sistema
bash: fg: sin control de trabajo
sistema PAUSE
@sistema PAUSE, set -m, creo.
extraño
Tengo control de trabajo (set -m) en el shell de inicio de sesión. Esa es la 'm' en el contenido himBH de $ -, pero parece desaparecer para las subcapas. Posiblemente un artefacto Cygwin. quejarse
PAUSA del sistema
No use "fg" en los scripts. Lea "ayuda esperar".
lhunath
8

el tiempo de espera es probablemente el primer enfoque para intentar. Es posible que necesite una notificación u otro comando para ejecutar si se agota el tiempo de espera. Después de bastante buscar y experimentar, se me ocurrió este script bash :

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
usuario2099484
fuente
5

Script simple con claridad de 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

Se agota el tiempo de espera de un comando que se ejecuta demasiado tiempo:

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

Termina inmediatamente para un comando que completa:

$ run 10 sleep 2
$

fuente
3

Si ya sabe el nombre del programa (supongamos program) que finalizará después del tiempo de espera (como ejemplo, 3segundos), puedo contribuir con una solución alternativa simple y algo sucia:

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

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

loup
fuente
2
Esto mata otros procesos que utilizan el nombre y no elimina el proceso con el nombre dado si cambia su nombre (por ejemplo, escribiendo en argv[0], quizás con otros hacks para hacer más espacio).
Jed
Encontré una variación de esto útil al intentar detener los contenedores acoplables después de un cierto tiempo. El cliente de Docker no parecía aceptar TERM / INT / KILL de una manera que realmente detuviera el contenedor que se estaba ejecutando en primer plano. Así que darle un nombre al contenedor y usarlo (sleep 3 && docker stop <container>) &funcionó muy bien. ¡Gracias!
Mat Schaffer
sí, es sucio y limitado, pero se puede mejorar de la siguiente manera: de { sleep 5 && kill -9 $(ps -fe | grep "program" | grep $$ | tr -s " " | cut -d" " -f2); } & SLEEPPID=$!; bash -c "program" && kill -9 $SLEEPPID"esta manera, matará solo trabajos en el shell actual.
ton
2

También está cratimeoutMartin 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'
max32
fuente
"En particular, conserva el comportamiento de la señal". ¡Es bueno tener esa opción!
PAUSA del sistema
1

OS X todavía no usa bash 4, ni tiene / usr / bin / timeout, así que aquí hay una función que funciona en OS X sin home-brew o macports que es similar a / usr / bin / timeout (basado en Tino's responder). La validación de parámetros, la ayuda, el uso y el soporte para 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
) }
NerdMachine
fuente
0

En el 99% de los casos, la respuesta NO es implementar ninguna lógica de tiempo de espera. La lógica de tiempo de espera es, en casi cualquier situación, una señal de advertencia roja de que algo más está mal y, en su lugar, debe solucionarse .

¿Su proceso se cuelga o se rompe después de n segundos a veces? Luego descubra por qué y solucione eso en su lugar.

Por otro lado, para hacer bien la solución de Strager, debe usar esperar "$ SPID" en lugar de fg 1, ya que en los scripts no tiene control de trabajo (e intentar activarlo es una estupidez). Además, fg 1 se basa en el hecho de que no inició ningún otro trabajo anteriormente en el script, lo cual es una mala suposición.

lhunath
fuente
44
Con acceso al 100% de la fuente (y la mayoría del hardware, como los conmutadores de red), estaría de acuerdo en que probablemente haya mejores soluciones que un tiempo de espera. Pero cuando un 'tlrbsf' es de código cerrado, solo binario, a veces hay que evitar esa limitación.
sistema PAUSE
@lhunath, "en los scripts no tienes control de trabajo (y tratar de encenderlo es una estupidez)" - Aclara aquí: stackoverflow.com/questions/690266/…
system PAUSE
@ PAUSE del sistema: la respuesta stackoverflow.com/questions/690266/… es correcta, también lo comenté.
lhunath
29
Ah, lo que dices no tiene sentido. Hay toneladas de casos en los que el tiempo de espera es una buena opción, por ejemplo, cada vez que tiene que pasar por la red.
Nate Murray
Una excepción: está escribiendo scripts de prueba para verificar si un software que ya se está ejecutando no tiene un problema de tiempo de espera.
peterh - Restablece a Monica el
0

Se me presentó un problema para preservar el contexto de shell y permitir tiempos de espera, el único problema con él es que detendrá la ejecución del script en el tiempo de espera, pero está bien con las necesidades que se me presentaron:

#!/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, supongo que había un directorio llamado scripts

mpapis
fuente
0
#! /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 "$@"
anguila ghEEz
fuente
@Tino Lo siento, olvidé por qué cambié la línea de finalización del proceso y por qué pensé que era importante compartir esto. Lástima que no escribí esto. Tal vez, descubrí que necesitaba hacer una pausa antes de buscar un éxito de kill -s TERM. El guión del año 2008 del libro de cocina parece verificar el estado del proceso inmediatamente después de enviar SIGTERM, lo que posiblemente conduzca a un error al intentar enviar SIGKILL a un proceso que murió.
anguila ghEEz
0

Mi problema fue quizás un poco diferente: inicio un comando a través de ssh en una máquina remota y quiero matar el shell y los niños si el comando se cuelga.

Ahora uso lo siguiente:

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

De esta forma, 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 los procesos de eliminación de una sesión ssh se manejan de manera diferente a un shell interactivo. Pero también puede usar la opción -t para ssh para asignar un pseudo terminal, por lo que actúa como un shell interactivo

Franky_GT
fuente
0

Aquí hay una versión que no se basa en generar un proceso hijo: necesitaba un script independiente que integrara esta funcionalidad. También realiza un intervalo de sondeo fraccional, por lo que puede sondear más rápido. se hubiera preferido el tiempo de espera, pero estoy atascado en un servidor antiguo

# 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
Neil McGill
fuente
-1

Una forma muy simplista:

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

Con pkill (opción -f ) puede matar su comando específico con argumentos o especificar -n para evitar matar el proceso anterior.

Francis
fuente
Te das cuenta de que esto es esencialmente lo que el OP tiene en su publicación y lo que indica que no quiere, ¿verdad? Porque siempre espera el retraso total del sueño.
Etan Reisner
-1

Sobre la base de la respuesta de @ loup ...

Si desea agotar el tiempo de espera de un proceso y silenciar la salida kill job / pid, ejecute:

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

Esto pone el proceso en segundo plano en una subshell para que no vea la salida del trabajo.

Cody A. Ray
fuente
-3

Tengo un trabajo cron que llama un script php y, a veces, se atasca en el script php. Esta solución fue perfecta para mí.

Yo suelo:

scripttimeout -t 60 /script.php
Jehad Buria
fuente
1
¿Qué es scripttimeout?
rudolfbyker