¿Cómo ejecutar un comando con un tiempo de espera para que se elimine si excede el umbral de tiempo de espera?

Resuelto system PAUSE asked hace 15 años • 24 respuestas

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

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

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

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

Así que este ingenioso enfoque de una sola línea tiene un par de problemas.
En primer lugar, sleepno es condicional, por lo que establece un límite inferior indeseable en el tiempo que tarda la secuencia en finalizar. Considere 30 segundos, 2 mo incluso 5 m para dormir, cuando el comando tlrbsf finaliza en 2 segundos, algo muy indeseable.
En segundo lugar, killes incondicional, por lo que esta secuencia intentará matar un proceso que no se está ejecutando y quejarse de ello.

Entonces...

¿Hay alguna manera de pausar el tiempo de espera de un comando que suele ejecutarse durante mucho tiempo pero a veces es rápido ( "tlrbsf" ) y que

  • tiene una implementación bash (la otra pregunta ya tiene respuestas en Perl y C)
  • terminará en el primero de los dos: terminación del programa tlrbsf o tiempo de espera transcurrido
  • no eliminará procesos no existentes o que no se están ejecutando (u, opcionalmente: no se quejará de una mala eliminación)
  • no tiene que ser una sola línea
  • puede ejecutarse bajo Cygwin o Linux

... y, para obtener puntos extra

  • ejecuta el comando tlrbsf en primer plano
  • cualquier 'suspensión' o proceso adicional en segundo plano

de modo que el stdin/stdout/stderr del comando tlrbsf pueda redirigirse, igual que si se hubiera ejecutado directamente?

Si es así, comparte tu código. Si no, por favor explica por qué.

Pasé un tiempo intentando piratear el ejemplo antes mencionado, pero estoy alcanzando el límite de mis habilidades de bash.

system PAUSE avatar Mar 27 '09 06:03 system PAUSE
Aceptado

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

timeout 5 /path/to/slow/command with options
yingted avatar Jan 03 '2011 03:01 yingted

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 avatar Mar 26 '2009 23:03 Juliano