Bash-Crashkurs: Skripting-Grundlagen
Bash-Crashkurs: Skripting-Grundlagen
Dieser Kurs vermittelt die Bash-Mechanismen, die für ein eigenes Skript zur Netzwerkkonfiguration gebraucht werden. Jeder Abschnitt führt einen Baustein ein. Am Ende schreibt ihr daraus selbst ein vollständiges Skript.
Variablensubstitution
Eine Variable wird ohne Leerzeichen um das = gesetzt:
NAME="server01"
Der Wert wird ausgelesen, indem man ein $ vor den Namen setzt – dieser Vorgang heißt Variablensubstitution: Bash ersetzt $NAME durch den gespeicherten Wert, bevor der Befehl ausgeführt wird.
echo $NAME
echo "$NAME"
In Anführungszeichen referenzieren, sonst gibt's bei Leerzeichen oder Sonderzeichen im Wert Probleme (Bash zerlegt den Wert sonst an Leerzeichen in mehrere Wörter).
- Übung
Setzt eine Variable ADDR mit dem Wert 10.2XX.0.10/24 und gebt sie einmal ohne und einmal mit Anführungszeichen aus.
Kommandosubstitution
Mit $(...) wird nicht der Text eines Befehls ausgeführt, sondern seine Ausgabe als Wert eingesetzt – das nennt man Kommandosubstitution:
DATUM=$(date +%Y-%m-%d)
echo "$DATUM"
$(date +%Y-%m-%d) wird zuerst ausgeführt, die Ausgabe des Befehls landet dann in DATUM.
- Übung
Speichert die Ausgabe von "hostname" in einer Variable HOST und gebt sie aus.
cut (Wiederholung aus 5101)
cut zerschneidet Text an einem Trennzeichen:
echo "server01.it2XX.int" | cut -d'.' -f1
# server01
echo "server01.it2XX.int" | cut -d'.' -f2-
# it2XX.int
-d'.' setzt den Punkt als Trennzeichen, -f1 nimmt das erste Feld, -f2- alles ab dem zweiten Feld bis zum Ende.
Kombiniert mit Kommandosubstitution lässt sich das Ergebnis direkt in eine Variable schreiben:
SHORT=$(echo "server01.it2XX.int" | cut -d'.' -f1)
- Übung
Zerlegt "server01.it2XX.int" mit cut und Kommandosubstitution in SHORT (server01) und DOM (it2XX.int).
Testausdrücke: [[ ]]
[[ ]] prüft eine Bedingung und liefert wahr oder unwahr (als Exit-Code, 0 = wahr, ungleich 0 = unwahr):
[[ "$X" == 1 ]] # wahr, wenn X gleich 1 ist
[[ -z "$VAR" ]] # wahr, wenn VAR leer ist
[[ -n "$VAR" ]] # wahr, wenn VAR NICHT leer ist
Der Exit-Code des zuletzt ausgeführten Befehls steht in $?. Damit lässt sich sichtbar machen, was [[ ]] tatsächlich zurückgibt:
X=1
[[ "$X" == 1 ]]
echo $?
# 0 (wahr)
[[ "$X" == 2 ]]
echo $?
# 1 (unwahr)
Das ist auch genau das, was if später intern auswertet: if prüft nichts anderes als $? des Befehls direkt danach.
Mehrere Bedingungen lassen sich mit || (ODER) oder && (UND) verknüpfen:
[[ -z "$FQDN" || -z "$ADDR" ]]
- Übung
Testet direkt auf der Kommandozeile: X=1 [[ "$X" == 1 ]] && echo "wahr" || echo "unwahr" X="" [[ -z "$X" ]] && echo "leer" || echo "nicht leer" Prüft danach jeweils mit echo $?, ob euer Ergebnis mit dem Exit-Code übereinstimmt.
if
[[ ]] allein tut noch nichts außer prüfen – erst if reagiert auf das Ergebnis:
if [[ -z "$FQDN" ]]; then
echo "FQDN fehlt!"
exit 1
fi
Aufbau: if BEDINGUNG; then ... fi (schließt den Block, "if" rückwärts geschrieben). Optional gibt es else für den Alternativfall und elif für eine weitere Bedingung.
if [[ -z "$FQDN" ]]; then
echo "FQDN fehlt!"
else
echo "FQDN ist $FQDN"
fi
- Übung
Schreibt ein if, das prüft, ob die Variable ADDR leer ist. Falls ja: Fehlermeldung ausgeben und mit exit 1 abbrechen.
while
while wiederholt einen Block, solange eine Bedingung wahr ist:
i=1
while [[ $i -le 3 ]]; do
echo "Durchlauf $i"
i=$((i+1))
done
while BEDINGUNG; do ... done. Wichtig: die Bedingung muss sich im Schleifenkörper irgendwann ändern, sonst läuft die Schleife endlos.
- Übung
Schreibt eine while-Schleife, die von 1 bis 5 zählt und jede Zahl ausgibt.
Heredoc
Ein Heredoc schreibt mehrzeiligen Text in eine Datei, ohne für jede Zeile ein eigenes echo zu brauchen:
cat <<HERE > /tmp/test.txt
Zeile 1
Zeile 2
HERE
Alles zwischen <<HERE und der abschließenden Zeile HERE wird an cat übergeben. > schreibt (überschreibt) die Datei, >> hängt an. Variablen werden innerhalb des Heredocs per Variablensubstitution ganz normal ersetzt:
cat <<HERE > /tmp/test.txt
Adresse: $ADDR
HERE
- Übung
Schreibt mit einem Heredoc eine Datei /tmp/netztest.txt, die die Werte von ADDR und DOM enthält.
Variablen übergeben über $1, $2, ...
Beim Aufruf eines Skripts landen die übergebenen Werte automatisch in nummerierten Variablen:
./skript.sh server01 10.2XX.0.10
echo "$1" # server01
echo "$2" # 10.2XX.0.10
$0 ist der Skriptname selbst, $1 das erste Argument, $2 das zweite, usw. $# gibt die Anzahl der übergebenen Argumente aus.
ℹ️ Hinweis: Diese Positionsparameter merken sich nur die Reihenfolge, nicht was gemeint ist. Der Aufrufer muss genau wissen, dass Argument 1 der FQDN ist und Argument 2 die Adresse. Vertauscht man die Reihenfolge, gibt es keine Fehlermeldung, nur falsche Werte.
- Übung
Schreibt ein Skript args.sh, das $1 und $2 ausgibt. Ruft es mit zwei, dann mit nur einem Argument auf. Was steht dann in $2?
Robuster mit getopts
Das Problem von $1/$2: feste Reihenfolge, keine Namen, kein "diese Option ist optional". getopts löst das, indem Optionen mit Buchstaben und in beliebiger Reihenfolge übergeben werden:
./skript.sh -f server01.it2XX.int -a 10.2XX.0.10/24
statt
./skript.sh server01.it2XX.int 10.2XX.0.10/24
Ein Options-String wie "f:a:" legt fest, welche Buchstaben erlaubt sind. Ein Doppelpunkt hinter einem Buchstaben bedeutet: diese Option erwartet einen Wert.
while getopts "f:a:" opt; do
case $opt in
f) FQDN="$OPTARG" ;;
a) ADDR="$OPTARG" ;;
*) echo "Ungültige Option" >&2; exit 1 ;;
esac
done
getopts liest bei jedem Schleifendurchlauf die nächste Option. Der Buchstabe landet in $opt, ein eventueller Wert automatisch in $OPTARG. Im case wird pro Buchstabe die passende Variable befüllt. Der Zweig *) fängt unbekannte Optionen ab; >&2 schickt die Fehlermeldung nach stderr, exit 1 beendet das Skript mit Fehlercode.
- Übung
Schreibt ein Skript foo.sh, das die Optionen -f und -a mit getopts einliest und beide Werte ausgibt. Testet danach: ./foo.sh -f ./foo.sh -z Was landet in $opt in beiden Fällen?
Abschlussaufgabe: Das vollständige Skript
Baut jetzt aus den Bausteinen oben ein Skript, das per Kommandozeile FQDN, IP-Adresse, Gateway und Nameserver entgegennimmt und daraus eine statische Netzwerkkonfiguration schreibt.
- Anforderungen
- Optionen
-f(FQDN),-a(Adresse/CIDR),-g(Gateway),-n(Nameserver), alle mit Wert, über getopts eingelesen - Fehlt eine der vier Angaben, Fehlermeldung mit Nutzungshinweis und
exit 1 - FQDN mit cut und Kommandosubstitution in
SHORTundDOMzerlegen /etc/network/interfacesper Heredoc schreiben (Loopback + statisches Interfaceenp0s3)- Hostname mit
hostnamectl set-hostnamesetzen
ℹ️ Hinweis: Testet euer Skript zuerst mit fehlenden Optionen und mit einer unbekannten Option, bevor ihr den vollständigen Aufruf prüft.