Jak uruchomić polecenie w mgnieniu oka aż do sukcesu?

237

Mam skrypt i chcę zapytać użytkownika o pewne informacje, skrypt nie może być kontynuowany, dopóki użytkownik nie wypełni tych informacji. Oto moja próba umieszczenia polecenia w pętli, aby to osiągnąć, ale z jakiegoś powodu to nie działa.

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

Próbowałem wielu odmian pętli while:

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

Ale nie mogę sprawić, żeby zadziałało.

JV
źródło

Odpowiedzi:

403
until passwd
do
  echo "Try again"
done

lub

while ! passwd
do
  echo "Try again"
done
Erik
źródło
20
Trudne z tego Ctrl-C.
DonGar,
Jeśli uważasz, że będziesz musiał to anulować, po prostu otwórz nową instancję swojej powłoki (na przykład wpisz „bash”), a kiedy chcesz ją anulować, przejdź do innego okna terminala i zabij nowo spawnowaną bash proces.
Ethan
50
Łatwy do Ctr-C z tego: until passwd; do echo "Try again"; sleep 2; done- wszystko co musisz zrobić, to nacisnąć Ctr-C zaraz po (w podanych dwóch sekundach), czy echowykonał swoją pracę.
Christian
9
@azmeuk: Spróbuj czegoś takiego until passwd || (( count++ >= 5 )); do echo "foo"; done(tylko bash, upewnij się, że ustawiłeś count na 0, jeśli ta zmienna istnieje) Jeśli potrzebujesz tego dla zwykłego sh, zwiększ licznik w ciele i użyj []
Justin Sane
2
Po wielokrotnym odwoływaniu się do tej strony postanowiłem utworzyć ogólną funkcję bash, aby ponowić polecenia, oto ona: gist.github.com/felipou/6fbec22c4e04d3adfae5
felipou
94

Zamiast tego musisz przetestować $?, co stanowi status wyjścia poprzedniego polecenia. passwdkończy się na 0, jeśli wszystko działało poprawnie, i niezerowym, jeśli zmiana hasła nie powiodła się (nieprawidłowe hasło, niedopasowanie hasła itp.)

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

W swojej wersji backtick porównujesz dane wyjściowe passwd, które byłyby podobne Enter passwordi confirm passwordpodobne.

Marc B.
źródło
2
Czy nie powinno tak być $??
Shawn Chin,
Podoba mi się to, ponieważ jest jasne, jak dostosować go do przeciwnej sytuacji. np. uruchom program z niedeterministycznym błędem, dopóki nie zawiedzie
Eric
Gdy sukces jest wskazywany przez niezerowy kod wyjścia, właśnie tego potrzebujesz.
Gudlaugur Egilsson
2
Myślę, że to może być nieco poprawić zmieniając pierwszą passwdze /bin/falsejeśli masz jakieś długie i skomplikowane polecenia, które nie chcą, aby dwie wersje.
coladict
Powinna być przyjęta odpowiedź imho. Powinno to działać w większości powłok (testowane w bash, mksh i ash) i można je łatwo dostosować do sytuacji.
linuxandria
73

Aby rozwinąć odpowiedź na @Marc B,

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

To dobry sposób na zrobienie tego samego, co nie jest specyficzne dla komendy.

duckworthd
źródło
11
Niestety nie działa (otrzymuję polecenie „!!”). Jak to ma działać?
Johannes Rudolph,
2
Uruchomienie poprzedniego polecenia jest bashową sztuczką. Na przykład, jeśli zapomnisz napisać sudoprzed poleceniem, możesz po prostu sudo !!uruchomić poprzednie polecenie z uprawnieniami administratora.
JohnEye,
1
czy mogę zrobić skrypt w moim profilu, który może to zrobić, więc mogę przejść: $ makelastwork
user230910 24.04.2016
2
Jak spać między biegami?
Kamil Dziedzic
6

Aby to osiągnąć, możesz użyć nieskończonej pętli:

while true
do
  read -p "Enter password" passwd
  case "$passwd" in
    <some good condition> ) break;;
  esac
done
kurumi
źródło
2
To jedyna odpowiedź, która nie wymagała umieszczenia mojego polecenia wieloliniowego w funkcji. Zrobiłem to && breakpo poleceniu weryfikacji zamiast wyboru przypadku.
carlin.scott 27.04.2018
4

Jeśli ktoś chce mieć limit ponownych prób:

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
źródło
1
until $(command)jest w zasadzie zawsze źle. Użyj until commandzamiast tego. $(...)Powoduje wyjście z commandsię sam uruchomić jako polecenie, a stan wyjścia tego polecenia wtórnym być co pętla zdecyduje się uruchomić lub nie na temat; to tylko wtedy, gdy jest nie stdout, że stan wyjścia samego polecenia jest uważany gdy substytucja komenda jest obecny.
Charles Duffy
$commandnie jest też wielkim symbolem zastępczym - zobacz BashFAQ # 50, dlaczego używanie ciągów do przechowywania poleceń jest wewnętrznie zawodne. Mimo to jest to lepsze niż było; wycofano głosowanie.
Charles Duffy
3
while [ -n $(passwd) ]; do
        echo "Try again";
done;
Andrés Rivas
źródło
3
To nie jest sposób, aby to zrobić ... negujesz stdout psswd, a następnie przeprowadzasz na nim jednoznaczny test. @duckworthd jest dobry.
Ray Foss
Co więcej, ponieważ nie ma cytowania, test będzie źle działał, gdy zostanie wydrukowany, ale zawiera więcej niż jedno słowo lub jest wyrażeniem globalnym pasującym do plików w bieżącym katalogu itp.
Charles Duffy