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?
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
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.
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
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.
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;
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
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`
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
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.
Utilisez trap
-
exit_()
{
exit
}
while true
do
echo 'running..'
trap exit_ int
done