scripts script resueltos programacion pasar parametros hacer espaƱol ejercicios ejemplos ejecutar como bash command-line timeout utilities

bash - programacion - scripts linux ejercicios resueltos



Tiempo de espera de un comando en bash sin demora innecesaria (22)

Aquí hay una versión que no se basa en generar un proceso secundario: necesitaba un script independiente que incorporara esta funcionalidad. También realiza un intervalo de sondeo fraccional, por lo que puede hacer una encuesta más rápida. Se habría 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

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

propone un método de 1 línea para vencer un comando de larga duración desde la línea de comandos de bash:

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

Pero es posible que un comando "de larga duración" 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).

Así que este ingenioso enfoque de 1 línea tiene un par de problemas. Primero, el sleep no es condicional, por lo que establece un límite inferior indeseable en el tiempo necesario para que la secuencia finalice. Considere 30s o 2m o incluso 5m para el reposo, cuando el comando tlrbsf finalice en 2 segundos, lo cual es altamente indeseable. Segundo, la kill es incondicional, por lo que esta secuencia intentará matar un proceso que no se está ejecutando y se quejará al respecto.

Asi que...

¿Hay alguna forma de que se agote un comando que normalmente dura mucho tiempo pero a veces es rápido ( "tlrbsf" ) que

  • tiene una implementación de bash (la otra pregunta ya tiene respuestas de Perl y C)
  • terminará al principio de los dos: terminación del programa tlrbsf , o tiempo de espera transcurrido
  • no eliminará los procesos que no existen ni los que se están ejecutando (o, opcionalmente, no se quejará de una mala muerte)
  • no tiene que ser un 1-liner
  • Puede ejecutarse bajo Cygwin o Linux

... y, para obtener puntos de bonificación, ejecuta el comando tlrbsf en primer plano y cualquier proceso de ''reposo'' o proceso adicional en segundo plano, de manera que el stdin / stdout / stderr del comando tlrbsf pueda redirigirse, igual que si hubiera correr directamente?

Si es así, por favor comparte tu 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.



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


En el 99% de los casos, la respuesta NO es implementar ninguna lógica de tiempo de espera. La lógica del tiempo de espera es, en casi cualquier situación, una señal de advertencia roja de que otra cosa está mal y debería corregirse.

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

Además, para resolver correctamente la solución de Strager, debe usar wait "$ SPID" en lugar de fg 1, ya que en los scripts no tiene control de trabajo (y tratar de activarlo es estúpido). Además, fg 1 se basa en el hecho de que no inició ningún otro trabajo previamente en el script, lo cual es una suposición errónea.


Esta solución funciona independientemente del modo de monitor de bash. Puedes usar la señal adecuada para terminar tu_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 a tu_comando después de un tiempo de espera dado; la secuencia de comandos espera la tarea lenta y termina el observador. Tenga en cuenta que la wait no funciona con procesos que son hijos de un shell diferente.

Ejemplos:

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

tu_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 terminó antes del tiempo límite (20 segundos)

tu_comando termino

( 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

puedes cambiar el SIGINT y el 10 como desees;)


Mi problema fue quizás un poco diferente: comienzo un comando a través de ssh en una máquina remota y quiero matar al shell y a los childs si el comando se bloquea.

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 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 matar procesos de una sesión ssh se maneja diferente de 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


OS X no usa bash 4 todavía, 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 ) }


Para slowcommand del slowcommand después de 1 segundo:

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


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

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

Es un poco más agradable que el "tiempo de espera" de coreutils porque imprime algo al finalizar el proceso, y también envía SIGKILL después de algún tiempo de forma predeterminada.


Probablemente estés buscando el comando timeout en coreutils. Dado que forma parte de coreutils, técnicamente es una solución C, pero sigue siendo coreutils. info timeout para más detalles. Aquí hay un ejemplo:

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


Puedes hacer esto completamente con bash 4.3 y superior:

_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 { _timeout 5 producer || echo KABOOM $?; } | consumer
  • Ejemplo: producer | { _timeout 5 consumer1; consumer2; } producer | { _timeout 5 consumer1; consumer2; }
  • Ejemplo: { while date; do sleep .3; done; } | _timeout 5 cat | less { while date; do sleep .3; done; } | _timeout 5 cat | less

  • Necesita Bash 4.3 para wait -n

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

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

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

Notas:

  • Estrictamente hablando no necesitas el ; en ; ) , sin embargo, hace que la cosa sea más consistente con el ; } ; } -caso Y el set +b probablemente también se puede dejar, pero es mejor prevenir que lamentar.

  • A excepción de --forground (probablemente) puede implementar todas las variantes de soporte de timeout . --preserve-status 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 volver a exportar las variables de entorno al shell adjunto de esta manera, naturalmente.

Editar:

Ejemplo del mundo real: Tiempo de espera __git_ps1 en caso de que __git_ps1 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. Noté que la exit 137 no es necesaria y hace que _timeout no _timeout confiable al mismo tiempo.

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

Edit4: Olvidé un _ en el primer _timeout para el ejemplo GIT del mundo real.


Se me presentó un problema para preservar el contexto de la shell y permitir tiempos de espera, el único problema es que detendrá la ejecución del script en el tiempo de espera, pero está bien con las necesidades que 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, asumo que había un directorio llamado scripts


Sencillo script 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

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 $


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

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

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


Sobre la base de la respuesta de @Loup ...

Si desea agotar el tiempo de espera de un proceso y silenciar la salida de trabajo / pid de eliminación, ejecute:

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

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


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


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

Yo suelo:

scripttimeout -t 60 /script.php


Un poco hacky, pero funciona. No funciona si tiene otros procesos en primer plano (¡ayúdenme 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


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.


tiempo de espera es probablemente el primer enfoque para tratar. Es posible que necesite una notificación u otro comando para ejecutar si se agota el tiempo. Después de un poco de búsqueda y experimentación, se me ocurrió este script de 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


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