Tengo un script bash que inicia un proceso secundario que se bloquea (en realidad, se bloquea) de vez en cuando y sin razón aparente (código cerrado, por lo que no hay mucho que pueda hacer al respecto). Como resultado, me gustaría poder iniciar este proceso durante un período de tiempo determinado y matarlo si no regresa con éxito después de un período de tiempo determinado.
¿Hay una manera simple y robusta de lograr eso usando bash?
PD: dime si esta pregunta es más adecuada para serverfault o superusuario.
Respuestas:
(Como se ve en: BASH FAQ entrada # 68: "¿Cómo ejecuto un comando y lo hago abortar (tiempo de espera) después de N segundos?" )
Si no le importa descargar algo, use
timeout
(sudo apt-get install timeout
) y úselo como: (la mayoría de los sistemas ya lo tienen instalado, de lo contrario usesudo apt-get install coreutils
)Si no desea descargar algo, haga lo que el tiempo de espera hace internamente:
En caso de que desee hacer un tiempo de espera para un código bash más largo, use la segunda opción como tal:
fuente
cmdpid=$BASHPID
no tomará el pid del shell de llamada sino el (primer) subshell que inicia()
. Lo(sleep
... llama a una segunda subshell dentro de la primera subshell para esperar 10 segundos en segundo plano y matar a la primera subshell que, después de haber lanzado el proceso de subshell asesino, procede a ejecutar su carga de trabajo ...timeout
forma parte de los coreutils de GNU, por lo que ya debería estar instalado en todos los sistemas GNU.timeout
ahora es parte de los coreutils.o para obtener los códigos de salida también:
fuente
kill -9
antes de probar las señales de que un proceso puede procesar primero.dosmth
termina en 2 segundos, otro proceso toma el viejo pid y matas al nuevo?fuente
sleep 999
aquí) a menudo termina más rápido que el sueño impuesto (sleep 10
)? ¿Qué sucede si deseo darle una oportunidad de hasta 1 minuto, 5 minutos? ¿Qué pasa si tengo un montón de estos casos en mi secuencia de comandos :)También tuve esta pregunta y encontré dos cosas más muy útiles:
Entonces uso algo como esto en la línea de comando (OSX 10.9):
Como se trata de un bucle, incluí un "sleep 0.2" para mantener fría la CPU. ;-)
(Por cierto: ping es un mal ejemplo de todos modos, solo usaría la opción incorporada "-t" (tiempo de espera)).
fuente
Suponiendo que tiene (o puede hacer fácilmente) un archivo pid para rastrear el pid del niño, puede crear un script que verifique el tiempo de modulación del archivo pid y elimine / reaparezca el proceso según sea necesario. Luego, simplemente coloque el script en crontab para ejecutarlo aproximadamente en el período que necesita.
Déjeme saber si usted necesita más detalles. Si eso no parece satisfacer sus necesidades, ¿qué pasa con el advenedizo?
fuente
Una forma es ejecutar el programa en una subshell y comunicarse con la subshell a través de una tubería con nombre
read
comando. De esta forma, puede verificar el estado de salida del proceso que se está ejecutando y comunicarlo nuevamente a través de la tubería.Aquí hay un ejemplo de tiempo de espera del
yes
comando después de 3 segundos. Obtiene el PID del proceso usandopgrep
(posiblemente solo funciona en Linux). También hay algún problema con el uso de una tubería, ya que un proceso que abre una tubería para la lectura se bloqueará hasta que también se abra para la escritura, y viceversa. Entonces, para evitar que elread
comando se cuelgue, he "acuñado" para abrir la tubería para leer con una subshell de fondo. (Otra forma de evitar una congelación para abrir la tubería de lectura-escritura, es decirread -t 5 <>finished.pipe
, sin embargo, eso también puede no funcionar, excepto con Linux).fuente
Aquí hay un intento que intenta evitar matar un proceso después de que ya haya salido, lo que reduce la posibilidad de matar otro proceso con la misma ID de proceso (aunque probablemente sea imposible evitar este tipo de error por completo).
Use like
run_with_timeout 3 sleep 10000
, que se ejecutasleep 10000
pero termina después de 3 segundos.Esto es como otras respuestas que usan un proceso de tiempo de espera en segundo plano para matar el proceso secundario después de un retraso. Creo que esto es casi lo mismo que la respuesta extendida de Dan ( https://stackoverflow.com/a/5161274/1351983 ), excepto que el shell de tiempo de espera no se eliminará si ya ha terminado.
Después de que este programa haya finalizado, todavía habrá algunos procesos de "suspensión" persistentes en ejecución, pero deberían ser inofensivos.
Esta puede ser una mejor solución que mi otra respuesta porque no usa la función de shell no portátil
read -t
y no la usapgrep
.fuente
(exec sh -c "$*") &
ysh -c "$*" &
? Específicamente, ¿por qué usar el primero en lugar del segundo?Aquí está la tercera respuesta que he enviado aquí. Éste maneja las interrupciones de señal y limpia los procesos en segundo plano cuando
SIGINT
se recibe. Utiliza el truco$BASHPID
yexec
utilizado en la respuesta superior para obtener el PID de un proceso (en este caso$$
en unash
invocación). Utiliza un FIFO para comunicarse con un subshell que es responsable de matar y limpiar. (Esto es como la tubería en mi segunda respuesta , pero tener una tubería con nombre significa que el manejador de señales también puede escribir en ella).He tratado de evitar las condiciones de carrera lo más que puedo. Sin embargo, una fuente de error que no pude eliminar es cuando el proceso finaliza casi al mismo tiempo que el tiempo de espera. Por ejemplo,
run_with_timeout 2 sleep 2
orun_with_timeout 0 sleep 0
. Para mí, este último da un error:ya que está tratando de matar un proceso que ya ha salido por sí mismo.
fuente