Wie können Sie einen Befehl in Bash bis zum Erfolg ausführen?

236

Ich habe ein Skript und möchte den Benutzer um einige Informationen bitten. Das Skript kann erst fortgesetzt werden, wenn der Benutzer diese Informationen eingibt. Das Folgende ist mein Versuch, einen Befehl in eine Schleife zu setzen, um dies zu erreichen, aber es funktioniert aus irgendeinem Grund nicht.

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

Ich habe viele Variationen der while-Schleife ausprobiert:

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

Aber ich kann es nicht zum Laufen bringen.

JV
quelle

Antworten:

403
until passwd
do
  echo "Try again"
done

oder

while ! passwd
do
  echo "Try again"
done
Erik
quelle
20
Es ist schwierig, Strg-C daraus zu ziehen.
DonGar
Wenn Sie glauben, dass Sie dies abbrechen müssen, öffnen Sie einfach eine neue Instanz Ihrer Shell (geben Sie beispielsweise "bash" ein) und gehen Sie, wenn Sie sie abbrechen möchten, in ein anderes Terminalfenster und beenden Sie die neu erzeugte Bash Prozess.
Ethan
50
Einfach zu Ctr-C: until passwd; do echo "Try again"; sleep 2; done- Alles, was Sie tun müssen, ist Ctr-C direkt nach (innerhalb der angegebenen zwei Sekunden) zu drücken, nachdem echoes seine Arbeit erledigt hat.
Christian
9
@azmeuk: Versuchen Sie etwas wie until passwd || (( count++ >= 5 )); do echo "foo"; done(nur Bash, stellen Sie sicher, dass die Anzahl auf 0 gesetzt wird, wenn diese Variable existiert). Wenn Sie dies für einfaches sh benötigen, erhöhen Sie den Zähler im Körper und verwenden Sie []
Justin Sane
2
Nachdem ich viele Male auf diese Seite verwiesen hatte
felipou
93

Sie müssen $?stattdessen testen , was der Exit-Status des vorherigen Befehls ist. passwdBeendet mit 0, wenn alles in Ordnung war, und ungleich Null, wenn die passwd-Änderung fehlgeschlagen ist (falsches Passwort, Passwort-Nichtübereinstimmung usw.)

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

Mit Ihrer Graviszeichen Version, sind Sie passwd die Ausgabe zu vergleichen, das Zeug wäre wie Enter passwordund confirm passwordund dergleichen.

Marc B.
quelle
2
Sollte das nicht sein $??
Shawn Chin
Ich mag das, weil es klar ist, wie man es an die gegenteilige Situation anpasst. zB ein Programm mit einem nicht deterministischen Fehler
Eric
Wenn der Erfolg durch einen Exit-Code ungleich Null angezeigt wird, ist dies das, was Sie benötigen.
Gudlaugur Egilsson
2
Ich denke, dies kann leicht verbessert werden, indem Sie den ersten passwdmit ändern, /bin/falsewenn Sie einen langen und komplizierten Befehl haben, von dem Sie nicht zwei Versionen behalten möchten.
Coladict
Sollte die akzeptierte Antwort imho sein. Dies sollte in den meisten Schalen funktionieren (getestet in Bash, Mksh und Asche) und ist leicht an die Situation anpassbar.
Linuxandria
72

Um auf die Antwort von @Marc B einzugehen,

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

Ist eine gute Möglichkeit, dasselbe zu tun, was nicht befehlsspezifisch ist.

duckworthd
quelle
11
Funktioniert bei mir leider nicht (ich bekomme den Befehl '!!' nicht gefunden). Wie soll es funktionieren?
Johannes Rudolph
2
Es ist ein Bash-Trick, den vorherigen Befehl auszuführen. Wenn Sie beispielsweise vergessen haben, sudovor einem Befehl zu schreiben , können Sie einfach sudo !!den vorherigen Befehl mit Root-Rechten ausführen.
JohnEye
1
Kann ich ein Skript in meinem Profil erstellen, das dies kann, damit ich gehen kann: $ makelastwork
user230910
2
Wie schlafe ich zwischen den Läufen?
Kamil Dziedzic
6

Sie können eine Endlosschleife verwenden, um dies zu erreichen:

while true
do
  read -p "Enter password" passwd
  case "$passwd" in
    <some good condition> ) break;;
  esac
done
kurumi
quelle
2
Dies ist die einzige Antwort, bei der mein mehrzeiliger Befehl nicht in eine Funktion eingefügt werden musste. Ich habe es jedoch && breaknach meinem Überprüfungsbefehl anstelle des Auswahlfalls getan .
carlin.scott
4

Wenn jemand ein Wiederholungslimit haben möchte:

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
quelle
1
until $(command)ist grundsätzlich immer falsch. Verwenden Sie until commandstattdessen. Dies $(...)bewirkt, dass die Ausgabe von commandselbst als Befehl ausgeführt wird und der Exit-Status dieses sekundären Befehls dem entspricht , auf dem die Schleife ausgeführt wird oder fehlschlägt. Nur wenn kein Standard vorhanden ist , wird der Exit-Status des Befehls selbst berücksichtigt, wenn die Befehlsersetzung vorhanden ist.
Charles Duffy
$commandist auch kein guter Platzhalter - siehe BashFAQ # 50, warum die Verwendung von Zeichenfolgen zum Speichern von Befehlen von Natur aus unzuverlässig ist. Trotzdem ist das besser als es war; Gegenstimme zurückgezogen.
Charles Duffy
3
while [ -n $(passwd) ]; do
        echo "Try again";
done;
Andrés Rivas
quelle
3
Dies ist nicht der richtige Weg ... Sie negieren die Standardausgabe von psswd und führen dann einen unären Test durch. @ duckworthd ist gut.
Ray Foss
Da kein Anführungszeichen vorhanden ist, verhält sich der Test bei der Ausgabe schlecht, es handelt sich jedoch um mehr als ein Wort oder um einen Glob-Ausdruck, der mit Dateien im aktuellen Verzeichnis übereinstimmt, oder so weiter.
Charles Duffy