Terminando un ciclo infinito


52

Tengo un comando que deseo ejecutar automáticamente cada vez que finaliza, así que ejecuté algo como esto:

while [ 1 ]; do COMMAND; done;

pero si no puedo detener el ciclo Ctrl-cya que eso simplemente mata COMMANDy no todo el ciclo.

¿Cómo lograría algo similar pero que pueda detener sin tener que cerrar el terminal?


11
Si estoy en bash, solo uso Ctrl-Z para detener el trabajo y luego "kill% 1" para matarlo.
Paul Cager

3
Solo espera ... Linus fue citado diciendo: "Todos sabemos que Linux es genial ... hace bucles infinitos en 5 segundos" . Así que realmente ... solo espera unos segundos más, debería completarse.
lornix

¡@PaulCager también funcionó para mí! ¿Por qué funciona donde Ctrl-C no funciona?
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

@cirosantilli mata el trabajo externo (el "envoltorio" de bash). En algunas situaciones, no matará de inmediato al "COMANDO", por ejemplo, si lo ocultas, puede escabullirse vivo incluso si su padre está muerto. Pero el ciclo está muerto, y esa es la parte importante.
orion

Respuestas:


38

Verifique el estado de salida del comando. Si el comando fue terminado por una señal, el código de salida será 128 + el número de señal. De la documentación en línea de GNU para bash :

Para los propósitos del shell, un comando que sale con un estado de salida cero ha tenido éxito. Un estado de salida distinto de cero indica un fallo. Este esquema aparentemente contraintuitivo se utiliza, por lo que hay una forma bien definida de indicar el éxito y una variedad de formas de indicar varios modos de falla. Cuando un comando termina en una señal fatal cuyo número es N, Bash usa el valor 128 + N como el estado de salida.

POSIX también especifica que el valor de un comando que terminó con una señal es mayor que 128, pero no parece especificar su valor exacto como lo hace GNU:

El estado de salida de un comando que finalizó porque recibió una señal se informará como mayor que 128.

Por ejemplo, si interrumpe un comando con control-C, el código de salida será 130, porque SIGINT es la señal 2 en los sistemas Unix. Entonces:

while [ 1 ]; do COMMAND; test $? -gt 128 && break; done

99
Cabe mencionar que esto no está garantizado, de hecho, muchas aplicaciones no lo harán.
Chris Down

1
@Kyle Jones: ¿puede vincular a los documentos POSIX / GNU que mencionan eso?
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

@cirosantilli Hecho.
Kyle Jones

@KyleJones gracias! Todavía en la práctica no funciona para COMMAND = paplay alert.ogg, ¿quizás porque paplaymaneja la señal?
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

@cirosantilli Sí, esa es la razón. Si un proceso maneja la señal y se cierra, eso es diferente al proceso que termina con una señal no manejada.
Kyle Jones

49

Puede detener y poner su trabajo en segundo plano mientras se ejecuta con ctrl+ z. Entonces puedes matar tu trabajo con:

$ kill %1

Donde [1] es su número de trabajo.


Vea también esta respuesta para explicaciones y más.
Skippy le Grand Gourou

2
Esta respuesta relativamente reciente simplemente funciona. Necesita ser votado. +1
shivams

Me ayudaste mucho. Esto es lo que he buscado en esta pregunta :)
Maximilian Ruta

18

Diría que sería mejor poner su bucle infinito en un script y manejar las señales allí. Aquí hay un punto de partida básico . Estoy seguro de que querrás modificarlo para adaptarlo. El script usa trappara atrapar ctrl- c(o SIGTERM), elimina el comando (lo he usado sleepaquí como prueba) y sale.

cleanup ()
{
kill -s SIGTERM $!
exit 0
}

trap cleanup SIGINT SIGTERM

while [ 1 ]
do
    sleep 60 &
    wait $!
done

44
Agradable. Así es como usé este consejo para hacer un reinicio automático de netcat wrapper:trap "exit 0" SIGINT SIGTERM; while true; do netcat -l -p 3000; done
Douglas

2
si se agrega este trapacercamiento a la misma secuencia de comandos (bash) con el bucle infinito de ser muertos, su uso $$en lugar de $!(ver aquí )
ardnew


8

Si ejecuta bash con -eél, saldrá en cualquier condición de error:

#!/bin/bash -e
false # returns 1
echo This won't be printed

8

¿Por qué no simplemente?

while [ 1 ]; do COMMAND || break; done;

O cuando se usa en un script,

#!/bin/bash
while [ 1 ]; do
  # ctrl+c terminates COMMAND and exits the while loop
  # (assuming COMMAND responds to ctrl+c)
  COMMAND || break
done;

1
Solución muy elegante. ¿Pero no funcionaría esto solo si COMMAND siempre devuelve un estado de salida exitoso?
howardh

Sí @howardh, eso es correcto.
Dale Anderson

3

Prefiero otra solución:

touch .runcmd; while [ -f ".runcmd" ]; do COMMAND; sleep 1; done

Para matar el ciclo, solo haz:

rm .runcmd && kill `pidof COMMAND`

2
  1. Siempre puede matar un proceso usando su PID, no hay necesidad de cerrar su terminal
  2. Si desea ejecutar algo en un bucle infinito como un demonio, lo mejor es ponerlo en segundo plano
  3. while : creará un bucle infinito y te ahorrará escribiendo el [ 1 ]

    while :; do COMMAND; done &

Esto imprimirá el PID. Si sale de su solicitud utilizando, ctrl+dentonces el trabajo en segundo plano no se cerrará, y luego puede eliminar el trabajo desde cualquier lugar utilizandokill PID

Si pierde el rastro de su PID, puede usarlo pstree -pa $USERo pgrep -fl '.*PROCESS.*'ayudarlo a encontrarlo


0

Uso trap-

exit_()
{
    exit
}

while true
do
    echo 'running..'
    trap exit_ int
done
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.