Comment pouvez-vous exécuter une commande dans bash over jusqu'à ce qu'elle réussisse

237

J'ai un script et je souhaite demander des informations à l'utilisateur. Le script ne peut pas continuer tant que l'utilisateur n'a pas renseigné ces informations. Ce qui suit est ma tentative de mettre une commande dans une boucle pour y parvenir, mais cela ne fonctionne pas pour une raison quelconque.

echo "Please change password"
while passwd
do
echo "Try again"
done

J'ai essayé de nombreuses variantes de la boucle while:

while `passwd`
while [[ "`passwd`" -gt 0 ]]
while [ `passwd` -ne 0 ]]
# ... And much more

Mais je n'arrive pas à le faire fonctionner.

JV
la source

Réponses:

403
until passwd
do
  echo "Try again"
done

ou

while ! passwd
do
  echo "Try again"
done
Erik
la source
20
Difficile de Ctrl-C hors de cela.
DonGar
Si vous pensez que vous allez devoir annuler cela, ouvrez simplement une nouvelle instance de votre shell (par exemple, tapez "bash") et, lorsque vous voulez l'annuler, allez dans une autre fenêtre de terminal et tuez le bash nouvellement généré processus.
Ethan
50
Facile à Ctr-C à partir de cela: until passwd; do echo "Try again"; sleep 2; done- tout ce que vous avez à faire est d'appuyer sur Ctr-C juste après (dans les deux secondes) le echotravail est fait.
Christian
9
@azmeuk: Essayez quelque chose comme until passwd || (( count++ >= 5 )); do echo "foo"; done(bash uniquement, assurez-vous de mettre le compte à 0 si ce variable existe) Si vous en avez besoin pour sh simple, incrémentez le compteur dans le corps et utilisez []
Justin Sane
2
Après avoir fait référence à cette page plusieurs fois, j'ai décidé de créer une fonction bash générique pour réessayer les commandes, la voici: gist.github.com/felipou/6fbec22c4e04d3adfae5
felipou
94

Vous devez tester à la $?place, qui est l'état de sortie de la commande précédente. passwdsort avec 0 si tout a bien fonctionné, et non nul si le changement de mot de passe a échoué (mot de passe incorrect, incompatibilité de mot de passe, etc ...)

passwd
while [ $? -ne 0 ]; do
    passwd
done

Avec votre version backtick, vous comparez la sortie de passwd, ce qui serait des choses comme Enter passwordet confirm passwordet autres.

Marc B
la source
2
N'est-ce pas $??
Shawn Chin
J'aime ça parce qu'il est clair comment l'adapter à la situation inverse. par exemple, exécuter un programme avec un bogue non déterministe jusqu'à ce qu'il échoue
Eric
Lorsque le succès est indiqué par un code de sortie non nul, c'est ce dont vous avez besoin.
Gudlaugur Egilsson
2
Je pense que cela peut être légèrement amélioré en changeant la première passwdavec /bin/falsesi vous avez une commande longue et compliquée dont vous ne voulez pas garder deux versions.
coladict du
Doit être la réponse acceptée à mon humble avis. Cela devrait fonctionner dans la plupart des coquilles (testé en bash, mksh et ash) et est facilement adaptable à la situation.
linuxandria
73

Pour développer la réponse de @Marc B,

$ passwd
$ while [ $? -ne 0 ]; do !!; done

C'est une bonne façon de faire la même chose qui n'est pas spécifique à une commande.

duckworthd
la source
11
Ne fonctionne pas pour moi malheureusement (je reçois la commande '!!' introuvable). Comment est-il censé fonctionner?
Johannes Rudolph
2
C'est une astuce bash pour exécuter la commande précédente. Par exemple, si vous oubliez d'écrire sudodevant une commande, vous pouvez simplement sudo !!exécuter la commande précédente avec les privilèges root.
JohnEye
1
puis-je faire un script dans mon profil qui peut le faire, donc je peux aller: $ makelastwork
user230910
2
Comment dormir entre les runs?
Kamil Dziedzic
6

Vous pouvez utiliser une boucle infinie pour y parvenir:

while true
do
  read -p "Enter password" passwd
  case "$passwd" in
    <some good condition> ) break;;
  esac
done
kurumi
la source
2
C'est la seule réponse qui n'a pas nécessité de mettre ma commande multi-ligne dans une fonction. Je l'ai fait && breakaprès ma commande de vérification au lieu du cas sélectionné.
carlin.scott
4

Si quelqu'un souhaite avoir une limite de nouvelles tentatives:

max_retry=5
counter=0
until $command
do
   sleep 1
   [[ counter -eq $max_retry ]] && echo "Failed!" && exit 1
   echo "Trying again. Try #$counter"
   ((counter++))
done
aclokay
la source
1
until $(command)est fondamentalement toujours faux. Utilisez until commandplutôt. Le $(...)fait que la sortie de commandsoit elle-même exécutée en tant que commande et que l'état de sortie de cette commande secondaire correspond à ce que la boucle décide d'exécuter ou d'échouer; il est seulement s'il est pas stdout que l'état de sortie de la commande elle - même est pris en compte lors de la substitution de commande est présente.
Charles Duffy
$commandn'est pas non plus un grand espace réservé - voir BashFAQ # 50 sur pourquoi l'utilisation de chaînes pour stocker des commandes est intrinsèquement peu fiable. Pourtant, c'est mieux qu'il ne l'était; downvote s'est rétracté.
Charles Duffy
3
while [ -n $(passwd) ]; do
        echo "Try again";
done;
Andrés Rivas
la source
3
Ce n'est pas la façon de le faire ... vous annulez la sortie standard de psswd et faites ensuite un test unaire dessus. @duckworthd est bon.
Ray Foss
De plus, parce qu'il n'y a pas de citer, le test va mal se conduire quand il est sortie , mais il est plus d'un mot, ou une expression de glob que les fichiers de correspondances dans le répertoire courant, ou ainsi de suite.
Charles Duffy