2.6 Bash
Wenn wir uns über SSH mit dem Server verbinden, landen wir automatisch in der Bourne-again shell (Bash). Die Bash ist der Kommandozeileninterpreter des Terminalfensters unseres Linux-Servers.
Bash steht für "Bourne-again shell" und ist eine verbesserte Version der ursprünglichen Unix-Shell, der Bourne-Shell (sh) von Stephen Bourne. Die Bash wurde 1989 entwickelt, um die Funktionalität der Bourne-Shell zu erweitern und gleichzeitig kompatibel mit dieser zu bleiben. Sie bietet interaktive Kommandozeilenfunktionen und Skripting-Fähigkeiten, die das Automatisieren von Aufgaben und die Verwaltung von Systemen erleichtern. Die Bash ist heute die am weitesten verbreitete Shell in der Unix-Welt und wird auf den meisten Linux-Systemen standardmäßig verwendet.
Die Bash ist also ein Kommandozeileninterpreter, der die vom Benutzer eingegebenen Befehle liest und ausführt. Dieselben Befehle können wir auch in eine Datei schreiben und nacheinander ausführen lassen. Eine solche Datei heißt Bash-Skript. Auf dieser Seite lernen wir Schritt für Schritt die Bausteine kennen und wenden sie in vier kleinen Beispielskripten an, die im Praktikum weiterverwendet werden.
Grundlegende Bash-Konzepte
Einige Bash-Konzepte kennen wir bereits.
-
Pipelines ermöglichen es, die Ausgabe eines Befehls direkt als Eingabe für einen anderen Befehl zu verwenden, was durch das
|-Zeichen erreicht wird. -
Umleitungen verwenden
>,>>,<,2>und&>Zeichen, um die Standardausgabe, Standardeingabe und Fehlerausgabe umzuleiten. -
Job-Kontrolle in der Bash ermöglicht es, Prozesse im Hintergrund (
&) oder Vordergrund (fg) laufen zu lassen und laufende Jobs zu verwalten (jobs).Beispiel
Der Befehl
sleep 100 &startet densleep-Prozess im Hintergrund. Mitjobswerden alle laufenden Jobs angezeigt undfg %1holt den ersten Job in den Vordergrund. Eine ausführliche Erklärung mit Beispielausgaben steht in 2.5.8 Prozessmanagement. -
Aliase sind Kurzbefehle, die längere Befehle oder Befehlskombinationen abkürzen.
Beispiel
Hier ein Alias zu einem komplexeren Befehl
alias counthtml='find /var/www/html -type f -name "*.html" | wc -l'.Wir können nach der Definition in die Kommandozeile einfach
counthtmlschreiben und damit den gesamten Befehl ausführen. Aber dieser Alias ist nur bis zum nächsten Ausloggen definiert. Um den Alias dauerhaft verwenden zu können, müssen wir die Datei.bashrceditieren, die im Heimatverzeichnis eines jeden Users liegt.Hierzu die Konfigurationsdatei öffnen
nano ~/.bashrcund ans Ende der Datei den o.g. Alias schreiben.Um die neue Konfiguration
.bashrcder Bash bekannt zu geben, muss man diese mitsource ~/.bashrcaufrufen.Nerd-Wissen: man kann mit Hilfe von
echound der richtigen Umleitung einfach den Alias-Befehl ans Ende der.bashrcschreiben. Probieren Sie es aus, aber machen Sie sich vorher ein Backup der Datei.
Variablen
Variablen werden verwendet, um Werte zu speichern, die später im Skript oder in der Kommandozeile verwendet werden können. Variablen werden ohne Leerzeichen um das Gleichheitszeichen definiert.
Beispiel
Hier wird die Variable benutzername auf "student" gesetzt und dann mit echo ausgegeben. Beachten Sie, dass beim Aufrufen der Variablen das $-Zeichen verwendet wird.
Umgebungsvariablen sind spezielle Variablen, die vom System verwendet werden, um Informationen über die Systemumgebung zu speichern. Beispiele sind PATH, HOME und USER.
Beispiel
Diese Befehle zeigen die Werte der Umgebungsvariablen PATH, HOME und USER an.
Sehr häufig wird die Ausgabe eines Befehls in einer Variablen gespeichert. Dazu wird der Befehl in $( ) geschrieben.
Beispiel
Zuerst wird date +%Y-%m-%d ausgeführt, z. B. mit dem Ergebnis 2026-10-09. Dieses Ergebnis wird in der Variablen heute gespeichert.
Variablen immer in Anführungszeichen
Schreiben Sie Variablen in doppelte Anführungszeichen, also "$datei" statt $datei. Enthält der Wert ein Leerzeichen, z. B. Mein Bericht.txt, würde die Bash ihn sonst als zwei getrennte Wörter behandeln. In einfachen Anführungszeichen ('$datei') wird die Variable dagegen gar nicht ersetzt.
Benutzereingaben
Mit read wird eine Eingabe von der Tastatur in eine Variable gelesen. Das funktioniert direkt in der Kommandozeile und später genauso in Skripten.
Beispiel
Nach read name wartet die Bash auf eine Eingabe. Was eingetippt und mit der Eingabetaste bestätigt wird, steht danach in der Variablen name. Beachten Sie, dass erst beim Aufrufen der Variablen das $-Zeichen verwendet wird.
Das erste Skript
Skripte sind Dateien, die eine Reihe von Bash-Befehlen enthalten und ausgeführt werden können, um Aufgaben zu automatisieren. Drei Dinge gehören immer dazu:
- In der ersten Zeile steht
#!/bin/bash(der sogenannte Shebang). Damit weiß das System, dass die Datei von der Bash ausgeführt werden soll. - Die Datei muss ausführbar sein:
chmod +x skript.sh. - Aufgerufen wird das Skript mit
./skript.sh. Das./bedeutet „im aktuellen Verzeichnis“. Ohne./sucht die Bash das Skript nur in den Verzeichnissen aus$PATH.
Zeilen, die mit # beginnen, sind Kommentare. Sie werden nicht ausgeführt und erklären, was das Skript tut. Die erste Zeile #!/bin/bash ist die einzige Ausnahme.
Beispielskript 1: info.sh
Das Skript gibt einen kurzen Bericht über das System aus. Legen Sie die Datei mit nano info.sh an:
#!/bin/bash
# info.sh - gibt einen kurzen Bericht über das System aus
benutzer=$(whoami)
rechner=$(hostname)
datum=$(date '+%d.%m.%Y %H:%M')
echo "Systembericht vom $datum"
echo "Benutzer: $benutzer"
echo "Rechner: $rechner"
echo "Speicherplatz auf /:"
df -h /
Ausführen:
Was passiert hier?
- Die Befehle
whoami,hostnameunddatewerden ausgeführt, ihre Ausgaben werden in drei Variablen gespeichert. echogibt die Variablen zusammen mit festem Text aus.df -h /wird direkt ausgeführt, seine Ausgabe erscheint ohne Umweg über eine Variable.
Fehlersuche mit bash -x
Wenn ein Skript nicht tut, was es soll, hilft der Aufruf bash -x info.sh. Die Bash zeigt dann vor jeder Zeile an, welchen Befehl sie mit welchen Werten ausführt (mit + markiert).
Parameter und Bedingungen
Einem Skript können beim Aufruf Parameter übergeben werden, z. B. ./skript.sh datei.txt ERROR. Im Skript stehen sie in besonderen Variablen:
| Variable | Bedeutung |
|---|---|
$1, $2, … |
der erste, zweite, … Parameter |
$# |
die Anzahl der übergebenen Parameter |
$0 |
der Name des Skripts selbst |
$? |
der Rückgabewert des letzten Befehls (0 bedeutet „erfolgreich“) |
Mit if führt ein Skript Befehle nur aus, wenn eine Bedingung erfüllt ist. Die Bedingung steht in eckigen Klammern, dabei sind die Leerzeichen innerhalb der Klammern wichtig.
#!/bin/bash
if [ "$1" -gt 10 ]; then
echo "Die Zahl ist größer als 10."
else
echo "Die Zahl ist 10 oder kleiner."
fi
Häufige Bedingungen:
| Bedingung | ist wahr, wenn … |
|---|---|
[ "$a" -eq "$b" ] |
die Zahlen gleich sind (-ne ungleich, -gt größer, -lt kleiner, -ge größer gleich, -le kleiner gleich) |
[ "$a" = "$b" ] |
die Texte gleich sind (!= ungleich) |
[ -f "$datei" ] |
die Datei existiert |
[ -d "$verz" ] |
das Verzeichnis existiert |
[ ! -f "$datei" ] |
die Datei nicht existiert (! kehrt jede Bedingung um) |
Mit exit wird ein Skript sofort beendet. exit 0 meldet Erfolg, exit 1 einen Fehler.
Beispielskript 2: zaehlen.sh
Das Skript zählt in einer Datei die Zeilen, die einen Suchbegriff enthalten. Datei und Suchbegriff werden als Parameter übergeben.
#!/bin/bash
# zaehlen.sh - zählt in einer Datei die Zeilen mit einem Suchbegriff
# Aufruf: ./zaehlen.sh DATEI SUCHBEGRIFF
if [ $# -ne 2 ]; then
echo "Aufruf: $0 DATEI SUCHBEGRIFF"
exit 1
fi
datei="$1"
begriff="$2"
if [ ! -f "$datei" ]; then
echo "Die Datei $datei gibt es nicht."
exit 1
fi
anzahl=$(grep -c "$begriff" "$datei")
gesamt=$(wc -l < "$datei")
echo "$anzahl von $gesamt Zeilen enthalten \"$begriff\"."
Beispielaufruf:
Die Ausgabe sieht dann z. B. so aus: 10 von 31 Zeilen enthalten "bash".
Was passiert hier?
- Zuerst prüft das Skript, ob genau zwei Parameter übergeben wurden. Wenn nicht, zeigt es an, wie es aufgerufen werden muss, und endet.
- Die Parameter werden in Variablen mit sprechenden Namen übernommen. Das macht das Skript lesbarer.
- Danach prüft es, ob die Datei existiert.
grep -czählt die Zeilen mit dem Suchbegriff,wc -l < "$datei"zählt alle Zeilen. Durch das<gibtwcnur die Zahl ohne den Dateinamen aus.
Schleifen und Rechnen
Mit Schleifen werden Befehle mehrfach ausgeführt.
-
Die for-Schleife durchläuft eine Liste von Werten:
Die Liste kann aus Zahlen, Wörtern oder Dateinamen bestehen, z. B.
for datei in *.txt; do. Statt1 2 3 4 5kann man auch{1..5}schreiben. -
Die while-Schleife läuft, solange eine Bedingung erfüllt ist:
Zum Rechnen schreibt man den Ausdruck in $(( )), z. B. summe=$((summe + anzahl)). Innerhalb der doppelten Klammern darf das $ vor den Variablen entfallen. Die Bash rechnet nur mit ganzen Zahlen.
Beispielskript 3: sortieren.sh
Das Skript räumt ein Verzeichnis auf. Es verschiebt Dateien nach ihrer Endung in Unterverzeichnisse, z. B. alle .jpg-Dateien nach jpg/.
#!/bin/bash
# sortieren.sh - verschiebt Dateien nach ihrer Endung in Unterverzeichnisse
# Aufruf: ./sortieren.sh VERZEICHNIS
verzeichnis="$1"
if [ ! -d "$verzeichnis" ]; then
echo "Das Verzeichnis $verzeichnis gibt es nicht."
exit 1
fi
cd "$verzeichnis"
summe=0
for endung in jpg txt md; do
anzahl=$(ls *."$endung" 2>/dev/null | wc -l)
if [ "$anzahl" -gt 0 ]; then
mkdir -p "$endung"
mv *."$endung" "$endung"/
echo "$anzahl Dateien mit der Endung .$endung nach $endung/ verschoben"
summe=$((summe + anzahl))
fi
done
echo "Insgesamt $summe Dateien verschoben."
Was passiert hier?
- Die for-Schleife läuft dreimal, die Variable
endunghat nacheinander die Wertejpg,txtundmd. ls *."$endung" | wc -lzählt die passenden Dateien.2>/dev/nullverwirft die Fehlermeldung vonls, falls es keine solche Datei gibt./dev/nullist eine Art „Papierkorb“ für Ausgaben.- Nur wenn es Dateien gibt, wird das Unterverzeichnis angelegt und werden die Dateien verschoben.
summezählt mit, wie viele Dateien insgesamt verschoben wurden.- Das
cdim Skript wirkt nur innerhalb des Skripts. Nach dem Ende befinden Sie sich wieder im selben Verzeichnis wie vorher.
Funktionen
Funktionen fassen mehrere Befehle unter einem Namen zusammen, der dann wie ein Befehl aufgerufen werden kann. Das lohnt sich, wenn dieselben Befehle an mehreren Stellen eines Skripts gebraucht werden.
#!/bin/bash
meine_funktion() {
echo "Dies ist eine Funktion."
}
meine_funktion # Ruft die Funktion auf
Bash-Skripte und Crontab
Eine wichtige Eigenschaft von Bash-Skripten ist, dass man sie mit crontab zeitgesteuert ausführen lassen kann (siehe 2.5.8). Ein typisches Beispiel ist ein Backup.
Beispielskript 4: backup.sh
Das Skript sichert ein Verzeichnis als komprimiertes Archiv mit Datum und Uhrzeit im Namen.
#!/bin/bash
# backup.sh - sichert ein Verzeichnis als komprimiertes Archiv
# Aufruf: ./backup.sh VERZEICHNIS
quelle="$1"
ziel="$HOME/backup"
archiv="$ziel/backup_$(date +%Y%m%d_%H%M).tar.gz"
if [ ! -d "$quelle" ]; then
echo "Fehler: $quelle ist kein Verzeichnis."
exit 1
fi
mkdir -p "$ziel"
tar -czf "$archiv" "$quelle" 2>/dev/null
if [ $? -eq 0 ]; then
echo "Backup erstellt: $archiv"
du -h "$archiv"
else
echo "Fehler beim Erstellen des Backups."
exit 1
fi
Was passiert hier?
- Der Name des Archivs enthält Datum und Uhrzeit, z. B.
backup_20261009_0200.tar.gz. So überschreibt ein neues Backup kein älteres. mkdir -plegt das Zielverzeichnis an, falls es noch nicht existiert.$?enthält den Rückgabewert vontar. Ist er0, war das Archivieren erfolgreich.du -hzeigt zum Schluss die Größe des Archivs an.
Um das Skript jeden Tag um 2 Uhr morgens für das Verzeichnis /var/www/html auszuführen, öffnen wir den Crontab-Editor mit crontab -e und fügen folgende Zeile hinzu:
Nerd-Wissen Cron-Scripting
Die Zeile 0 2 * * * /root/backup.sh /var/www/html im crontab-Editor bedeutet, dass das Skript backup.sh jeden Tag um 2 Uhr morgens ausgeführt wird.
0: Minute (0. Minute der Stunde)2: Stunde (2. Stunde des Tages, also 2 Uhr morgens)*: Tag des Monats (jeder Tag des Monats)*: Monat (jeder Monat)*: Wochentag (jeder Wochentag)
Das Skript /root/backup.sh wird somit täglich um 2:00 Uhr morgens ausgeführt. Im Crontab-Eintrag muss der vollständige Pfad zum Skript stehen, da Cron nicht im Heimatverzeichnis startet.