web-dev-qa-db-fra.com

Terminer une boucle infinie

J'ai une commande que je veux relancer automatiquement à chaque fois qu'elle se termine, j'ai donc exécuté quelque chose comme ceci:

while [ 1 ]; do COMMAND; done;

mais si je ne peux pas arrêter la boucle avec Ctrl-c car cela tue juste COMMAND et non la boucle entière.

Comment pourrais-je réaliser quelque chose de similaire mais que je peux arrêter sans avoir à fermer le terminal?

57
howardh

Vérifiez l'état de sortie de la commande. Si la commande a été interrompue par un signal, le code de sortie sera 128 + le numéro du signal. De la documentation en ligne GNU pour bash :

Pour les besoins du shell, une commande qui se termine avec un état de sortie nul a réussi. Un état de sortie différent de zéro indique un échec. Ce schéma apparemment contre-intuitif est utilisé, il existe donc un moyen bien défini d'indiquer le succès et une variété de façons d'indiquer divers modes de défaillance. Lorsqu'une commande se termine sur un signal fatal dont le numéro est N, Bash utilise la valeur 128 + N comme état de sortie.

POSIX spécifie également que la valeur d'une commande terminée par un signal est supérieure à 128, mais ne semble pas spécifier sa valeur exacte comme GNU fait:

L'état de sortie d'une commande qui s'est terminée parce qu'elle a reçu un signal doit être signalé comme supérieur à 128.

Par exemple, si vous interrompez une commande avec control-C, le code de sortie sera 130, car SIGINT est le signal 2 sur les systèmes Unix. Donc:

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

Vous pouvez arrêter et mettre votre travail en arrière-plan pendant son exécution à l'aide de ctrl+z. Ensuite, vous pouvez tuer votre travail avec:

$ kill %1

Où [1] est votre numéro de travail.

51
Jem

Je dirais qu'il serait préférable de mettre votre boucle infinie dans un script et d'y gérer les signaux. Voici un point de départ de base. Je suis sûr que vous voudrez le modifier en fonction. Le script utilise trap pour intercepter ctrl-c (ou SIGTERM), tue la commande (j'ai utilisé sleep ici comme test) et quitte.

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

trap cleanup SIGINT SIGTERM

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

Je tiens généralement juste Ctrl-C. Tôt ou tard, il s'enregistrera entre COMMAND et terminera ainsi la boucle while. Il y a peut-être une meilleure façon.

15
jw013

Pourquoi pas simplement,

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

Ou lorsqu'il est utilisé dans 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;
10
Dale Anderson

Si vous exécutez bash avec -e il se fermera dans toutes les conditions d'erreur:

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

Je préfère une autre solution:

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

Afin de tuer la boucle, faites simplement:

rm .runcmd && kill `pidof COMMAND`
3
M4tze
  1. Vous pouvez toujours tuer un processus en utilisant son PID, il n'est pas nécessaire de fermer votre terminal
  2. Si vous voulez exécuter quelque chose dans une boucle infinie comme un démon, il vaut mieux le mettre en arrière-plan
  3. while : créera une boucle infinie et vous évitera d'écrire le [ 1 ]

    while :; do COMMAND; done &
    

Cela imprimera le PID. Si vous quittez votre invite à l'aide de ctrl+d alors le travail en arrière-plan ne se fermera pas, et vous pourrez ensuite le supprimer de n'importe où en utilisant kill PID

Si vous perdez la trace de votre PID, vous pouvez utiliser pstree -pa $USER ou pgrep -fl '.*PROCESS.*' pour vous aider à le trouver

2
errant.info

Ce qui fonctionne assez bien pour moi, c'est:

while sleep 1; do COMMAND; done

Cela fonctionne car sleep 1 reste un moment et s'il obtient ctrl + c, il revient avec un zéro et la boucle se terminera.

1
Iwan Aucamp

Utilisez trap -

exit_()
{
    exit
}

while true
do
    echo 'running..'
    trap exit_ int
done
0
markroxor