Dies ist der zweite Artikel einer Serie, die sich auf Gnu Bash-Skripting konzentriert. Im ersten Bash-Skripting-Artikel haben wir gerade das einfachste Skript erstellt:einfache Befehle, einer nach dem anderen. Wir haben auch die Verwendung einiger Variablen gesehen. Dieser Artikel behandelt Bash-Kontrollstrukturen.
Kontrollstrukturen
Wie ich im vorherigen Artikel gesagt habe, ist Faulheit der Schlüssel zum Erfolg. Wenn ich einige Befehle ausführen muss, eine Weile später dieselben Befehle in derselben Reihenfolge, öffne ich beim dritten Mal einen Texteditor und füge diese Befehle ein, um sie als Skript zu speichern.
Nach ein paar Tagen beginnt dieses Skript zu wachsen, sowohl in der Erweiterung als auch in der Komplexität. Ich brauche Möglichkeiten, um den Ausführungsfluss zu steuern, zum Beispiel:
- wenn etwas passiert, tun Sie etwas, oder wenn etwas nicht passiert, tun Sie etwas anderes;
- während etwas passiert, tun Sie etwas immer wieder oder tun Sie etwas immer wieder;
- oder abhängig vom Wert einer Variablen oder dem, was passiert ist, tue dies oder jenes,
- usw.
Dafür sind Bash-Kontrollstrukturen gedacht (eigentlich in Bash oder einer anderen Programmiersprache). Aber zuerst müssen wir Folgendes wissen:
Exit-Status
Aus der Bash-Manpage:
«Der Exit-Status eines ausgeführten Befehls ist der Wert, der vom Waitpid-Systemaufruf oder einer entsprechenden Funktion zurückgegeben wird. Ausgangsstatus liegen zwischen 0 und 255. (…) Ein Exit-Status von Null zeigt einen Erfolg an (oder gleich „true“). Ein Exit-Status ungleich Null weist auf einen Fehler hin (oder gleich 'false').(…. Zum Beispiel:) Wenn ein Befehl nicht gefunden wird, gibt der zu seiner Ausführung erstellte untergeordnete Prozess den Status 127 zurück. Wenn ein Befehl gefunden wird, aber nicht ausführbar ist, der Rückgabestatus ist 126 ».
tl;dr:Ausgangsstatus von 0 bedeutet wahr , Erfolg; jeder andere Wert bedeutet falsch . Es ist wichtig, dies zu lernen, da das Treffen von Wahr/Falsch-Entscheidungen bedeutet, den Beendigungsstatus eines Befehls zu überprüfen.
Bash speichert den Exit-Status des zuletzt ausgeführten Befehls in $? Variable.
wenn-dann-sonst
Dies ist die falls etwas passiert (Etwas passiert bedeutet ein Befehl mit einem Exit-Status von Null) mach etwas , oder etwas anderes tun. Die Syntax lautet:
if command then command1 command2 ... else commandA commandB ... fi
Wie Sie auf dem Screenshot sehen können, kann es in einer einzigen Zeile geschrieben werden, wobei jeder Befehl durch ein Semikolon getrennt wird:
if command; then command1; command2;...; else commandA; commandB;...;fi
Abhängig von der Komplexität des Wenn-Dann-Blocks kann es einfacher oder schwerer zu lesen sein, den „Einzeiler“-Stil zu lesen.
Was ist, wenn Sie einen Befehl ausführen möchten, wenn etwas passiert, führen Sie einen anderen Befehl nur aus, wenn etwas anderes das passiert? Verwenden Sie das Schlüsselwort elif (else if):
if command then command1 command2 ... elif other_command commandA commandB ... else another block of commands fi
Ein If-Then-Else-Block endet mit dem Schlüsselwort fi (falls rückwärts).
String- und Zahlenvergleich
Erinnerst du dich an Variablen? häufig passiert etwas ist, dass wir abhängig vom Wert einer Variablen einige Befehle oder andere ausführen möchten.
Um zu vergleichen, ob ein String einem Wort gleicht oder eine Zahl niedriger als andere ist, verwenden wir den Befehl test . Die allgemeine Syntax lautet:
test expression
Wenn Ausdruck weggelassen wird, gilt es als falsch (Ausgangsstatus> 0). Wenn Ausdruck ist nur eine Textfolge, es ist wahr. Dies könnte z. B. knifflig sein:
$ test false; echo $? 0
Wir testen nicht den Exit-Status des Befehls false (es gibt einen Befehl false ), aber die Zeichenfolge false gegen nichts. Die andere Möglichkeit, den Testbefehl auszuführen, besteht darin, den Ausdruck in eckige Klammern einzuschließen. Dieser andere Weg ist innerhalb eines if-then-else-Blocks einfacher zu lesen.
Operanden | wahr, wenn |
Ausdruck | wahr |
! Ausdruck | false (negiert Ausdruck) |
AusdruckA -ein AusdruckB | Und. beide Ausdrücke sind wahr |
AusdruckA -o AusdruckB | Oder. Einer der Ausdrücke ist wahr |
Zeichenfolge1 =Zeichenfolge2 | beide Zeichenfolgen sind gleich |
Zeichenfolge1 !=Zeichenfolge2 | Strings sind unterschiedlich |
int1 -eq int2 | Ganzzahl1 ist gleich Ganzzahl2 |
int1-ge int2 | int1>=int2 |
int1 -gt int2 | int1> int2 |
int1 -le int2 | int1 <=int2 |
int1 -lt int2 | int1 |
int1 -ne int2 | int1 ist nicht gleich int2 |
-e-Datei | Datei existiert |
-d Verzeichnis | Datei existiert und ist ein Verzeichnis |
Es gibt noch mehr Ausdrücke, das sind die, die ich für wichtiger halte. Lesen Sie die Manpage für Test, um die anderen Ausdrücke zu lernen.
Achten Sie darauf, dass zwischen den beiden eckigen Klammern und dem Ausdruck ein Leerzeichen steht. Jetzt können wir test verwenden, um etwas zu tun basierend auf dem Wert einer Variablen. Zum Beispiel:
Fall
Wenn wir eine Variable mit mehreren Werten testen möchten, können wir unserem if-then-else-Block mehrere elif wie folgt hinzufügen:
if [ $a = "value1" ] then command1 for value1 ... commandN for value1 elif [ $a = "value2" ] then command1 for value2 ... commandN for value2 ... elif [ $a = "valueN" ] then command1 for valueN ... commandN for valueN else command1 for every other value ... commandN for every other value fi
Oder wir können es durch einen case-Befehl ersetzen, der einfacher zu lesen und zu schreiben ist:
case expression in value1) command1 for value1 ... commandN for value1 ;; value2) command1 for value2 ... commandN for value2 ;; ... *) command1 for every other value ... commandN for every other value ;; esac
Außerdem weniger Tastenanschläge, vergessen Sie nicht, faul genug zu sein, um den Computer für Sie arbeiten zu lassen. Der letzte Wert * ist der Standardfall, jeder andere Wert, der zuvor nicht abgeglichen wurde. Fall rückwärts, esac , ist analog zur fi-Anweisung. Wenn die ;; -Operator verwendet wird, werden nach der ersten Musterübereinstimmung keine weiteren Übereinstimmungen versucht. Um das nächste Muster zu testen, verwenden Sie ;;* Operator. Zum Beispiel:
während und bis
Während etwas immer und immer wieder getan wird, während etwas passiert. Und until ist dasselbe, aber negiert (etwas immer wieder tun, während etwas nicht passiert). Die allgemeine Syntax lautet:
while command do command1 command2 ... commandN done
Um etwas zu tun, während etwas nicht passiert (d. h. der Existent-Status ist von 0 verschieden), ersetzen Sie while durch until. Siehe einige Beispiele:
Beachten Sie, dass sie nicht die gleiche Ausgabe erzeugt haben. Wenn $a im While-Block den Wert 4 erreicht, ist er nicht kleiner als 4, sodass der Test als falsch ausgewertet wird (und der Exit-Code> 0 ist). Wenn $a im until-Block den Wert 4 erreicht, ist es nicht größer als 4.
Eine Uhr in Ihrem Terminal
Manchmal teste ich einen Cronjob und das macht mir Angst, bis er ausgeführt wird. Ich verwende den true-Befehl (der, wie Sie sich vielleicht gedacht haben, einen 0-Exit-Status erzeugt) als Bedingung und führe den Befehl date immer wieder aus:
while true do date sleep 1 clear done
Dies würde foverer laufen, aber ich kann mit ctrl-c unterbrechen.
für-in -tun
Diese Art von Schleife iteriert über eine bereitgestellte Liste, wobei jedes Element einer Variablen zugewiesen wird. Syntax:
for i in list of words do command1 ... commandN done
Dies ist in einem Beispiel einfacher zu sehen:
Bleib dran
Die Dinge werden immer komplexer, aber wir möchten, dass unsere Skripte einige Entscheidungen treffen, um dies oder jenes zu tun. Ich sollte andere Themen behandeln, aber dieser Artikel über Bash-Kontrollstrukturen ist zu lang geworden.
Bei den nächsten werde ich Pipes und Redirection behandeln. Ich werde wahrscheinlich mit den Beispielen auf diese Bash-Kontrollstrukturen zurückkommen, so etwas wie die Verwendung der Ausgabe eines Befehls in einer for-in-Schleife und solche Sachen.