Einen Shell-Befehl wiederholt ausführen, bis er fehlschlägt?

190

Ich habe einen Fuzzy- Test geschrieben, der unzuverlässig fehlschlägt. Ich habe Debug-Code hinzugefügt, aber jetzt möchte ich den Test ausführen, bis er fehlschlägt, damit ich die Debug-Ausgabe erfassen kann.

Ich habe den Test so eingerichtet, dass ich ihn ausführen kann mit:

./runtest

Meine aktuelle Lösung besteht darin, ein untilfailSkript zu schreiben :

#!/bin/bash
$@
while [ $? -eq 0 ]; do
    $@
done

Dann benutze es:

untilfail ./runtest

Gibt es eine einfachere Lösung?

bradley.ayers
quelle
11
Randnotiz: gewöhnlich "$ @" zitieren.
Jordan

Antworten:

326

while Nimmt einen Befehl zum Ausführen, damit Sie den einfacheren verwenden können

while ./runtest; do :; done

Dies stoppt die Schleife, wenn ./runtestein Exit-Code ungleich Null zurückgegeben wird (was normalerweise auf einen Fehler hinweist).

Um Ihre aktuelle Lösung weiter zu vereinfachen, sollten Sie einfach Ihr tillfail-Skript so ändern, dass es folgendermaßen aussieht:

#!/bin/bash

while "$@"; do :; done

Und dann können Sie es mit jedem Befehl aufrufen, den Sie bereits verwenden:

untilfail ./runTest --and val1,val2 -o option1 "argument two"
nneonneo
quelle
25
Es ist gut, auch darauf hinzuweisen, dass dies [ein Befehl ist. Es ist ein häufiges Missverständnis bei neuen Benutzern, [das Teil ifund whileSyntax ist.
Jordanm
2
Wie konnte ich zählen, wie oft es ausgeführt wurde, bevor es fehlschlug?
GrantJ
13
@GrantJ: Es ist eigentlich ganz einfach. Setzen count=0vor der Schleife, dann anstelle der :in der Schleife (a no-op), setzen (( count++ ))- dieses inkrementiert den Zähler.
Nneonneo
14
Ein Produktivitäts-Hack: Wenn Sie auf einem System mit sayund einem Lautsprecher sind, können Sie while ./runtest; do :; done && say test failedbenachrichtigt werden, wenn es jemals aufhört
Schneems
5
@Schneems: Erwähnenswert ist, dass sayes MacOS-spezifisch ist.
Nneonneo
13

Wenn Sie eine komplexe Pipeline nicht in ein Shell-Skript oder eine Shell-Funktion einbinden möchten, funktioniert dies wie folgt:

while true; do 
  curl -s "https:..." | grep "HasErrors.:true"
  if [[ "$?" -ne 0 ]]; then 
    break
  fi
  sleep 120
done

Die HTTP-Anforderung gibt in diesem Fall immer 200 zurück, gibt aber auch JSON mit dem Attribut "HasErrors" zurück: true, wenn ein Fehler auftritt.

Judd Rogers
quelle
1

Nachdem ich ein ähnliches Problem in einem System hatte, in dem die Shell-Wiederholungslogik überall dupliziert wurde, habe ich ein spezielles Tool zur Lösung dieses Problems namens "Wiederholungsversuch" entwickelt:

retry --until=fail ./runtest

Ein komplexeres Beispiel:

retry --until=fail --message="test succeeded" --delay=1 ./runtest

Tool verfügbar unter https://github.com/minfrin/retry .

Graham Leggett
quelle