Linux Professional Institute Learning Logo.
Weiter zum Inhalt
  • Home
    • Alle Ressourcen
    • LPI Lernmaterialien
    • Mitmachen
    • Publishing Partner
    • Publishing Partner werden
    • Über uns
    • FAQ
    • Mitwirkende
    • Übersetzungen
    • Kontakt
  • LPI.org
105.2 Lektion 2
Thema 105: Shells und Shell-Skripte
105.1 Die Shell-Umgebung anpassen und verwenden
  • 105.1 Lektion 1
  • 105.1 Lektion 2
  • 105.1 Lektion 3
105.2 Einfache Skripte anpassen oder schreiben
  • 105.2 Lektion 1
  • 105.2 Lektion 2
Thema 106: Benutzerschnittstellen und Desktops
106.1 X11 installieren und konfigurieren
  • 106.1 Lektion 1
106.2 Grafische Desktops
  • 106.2 Lektion 1
106.3 Barrierefreiheit
  • 106.3 Lektion 1
Thema 107: Administrative Aufgaben
107.1 Benutzer- und Gruppenkonten und dazugehörige Systemdateien verwalten
  • 107.1 Lektion 1
  • 107.1 Lektion 2
107.2 Systemadministrationsaufgaben durch Einplanen von Jobs automatisieren
  • 107.2 Lektion 1
  • 107.2 Lektion 2
107.3 Lokalisierung und Internationalisierung
  • 107.3 Lektion 1
Thema 108: Grundlegende Systemdienste
108.1 Die Systemzeit verwalten
  • 108.1 Lektion 1
  • 108.1 Lektion 2
108.2 Systemprotokollierung
  • 108.2 Lektion 1
  • 108.2 Lektion 2
108.3 Grundlagen von Mail Transfer Agents (MTA)
  • 108.3 Lektion 1
108.4 Drucker und Druckvorgänge verwalten
  • 108.4 Lektion 1
Thema 109: Netzwerkgrundlagen
109.1 Grundlagen von Internetprotokollen
  • 109.1 Lektion 1
  • 109.1 Lektion 2
109.2 Persistente Netzwerkkonfiguration
  • 109.2 Lektion 1
  • 109.2 Lektion 2
109.3 Grundlegende Netzwerkfehlerbehebung
  • 109.3 Lektion 1
  • 109.3 Lektion 2
109.4 Clientseitiges DNS konfigurieren
  • 109.4 Lektion 1
Thema 110: Sicherheit
110.1 Administrationsaufgaben für Sicherheit durchführen
  • 110.1 Lektion 1
110.2 Einen Rechner absichern
  • 110.2 Lektion 1
110.3 Daten durch Verschlüsselung schützen
  • 110.3 Lektion 1
  • 110.3 Lektion 2
How to get certified
  1. Thema 105: Shells und Shell-Skripte
  2. 105.2 Einfache Skripte anpassen oder schreiben
  3. 105.2 Lektion 2

105.2 Lektion 2

Zertifikat:

LPIC-1

Version:

5.0

Thema:

105 Shells und Shell-Skripte

Lernziel:

105.2 Einfache Skripte anpassen oder schreiben

Lektion:

2 von 2

Einführung

Shellskripte sind im Allgemeinen dazu gedacht, Vorgänge im Zusammenhang mit Dateien und Verzeichnissen zu automatisieren, also dieselben Vorgänge, die Sie auch manuell an der Kommandozeile ausführen könnten. Die Reichweite von Shellskripten ist jedoch nicht auf die Dokumente eines Benutzers beschränkt, denn Konfiguration und Interaktion mit vielen Aspekten eines Linux-Systems erfolgen über Skriptdateien.

Die Bash bietet viele eingebaute Befehle zum Schreiben von Shellskripten, aber die volle Leistungsfähigkeit dieser Skripte entfaltet sich erst in deren Kombination mit den vielen auf der Kommandozeile eines Linux-System verfügbaren Dienstprogrammen.

Erweiterte Tests

Bash als Skriptsprache ist hauptsächlich auf die Arbeit mit Dateien ausgerichtet, daher verfügt der in der Bash eingebaute Befehl test über viele Optionen zur Auswertung der Eigenschaften von Dateisystemobjekten (v.a. Dateien und Verzeichnisse). Tests für Dateien und Verzeichnisse prüfen beispielsweise, ob die Dateien und Verzeichnisse, die für eine bestimmte Aufgabe benötigt werden, vorhanden und lesbar sind. Ist der Test erfolgreich, werden die entsprechenden Aktionen über eine if-Anweisung ausgeführt.

Der Befehl test kennt zwei Syntaxformen zur Auswertung von Testausdrücken: als Argument für den Befehl test oder in eckigen Klammern, wobei der Befehl test implizit genutzt wird. Den Test, ob /etc ein gültiges Verzeichnis ist, schreiben Sie also auf eine der folgenden Arten:

$ test -d /etc
$ echo $?
0
$ [ -d /etc ]
$ echo $?
0

Wie durch die Exit-Statuscodes in der speziellen Variable $? bestätigt – der Wert 0 bedeutet, dass der Test erfolgreich war –, haben beide Formen /etc als gültiges Verzeichnis ausgewertet. Unter der Annahme, dass der Pfad zu einer Datei oder einem Verzeichnis in der Variablen $VAR gespeichert wurde, können Sie die folgenden Ausdrücke als Argumente für test oder innerhalb eckiger Klammern verwenden:

-a "$VAR"

Prüft, ob der Pfad in VAR im Dateisystem existiert und ob es sich um eine Datei handelt.

-b "$VAR"

Prüft, ob der Pfad in VAR eine blockorientierte Gerätedatei ist.

-c "$VAR"

Prüft, ob der Pfad in VAR eine zeichenorientierte Gerätedatei ist.

-d "$VAR"

Prüft, ob der Pfad in VAR ein Verzeichnis ist.

-e "$VAR"

Prüft, ob der Pfad in VAR im Dateisystem existiert.

-f "$VAR"

Prüft, ob der Pfad in VAR existiert und ob es sich um eine reguläre Datei handelt.

-g "$VAR"

Prüft, ob der Pfad in VAR die SGID-Berechtigung hat.

-h "$VAR"

Prüft, ob der Pfad in VAR ein symbolischer Link ist.

-L "$VAR"

Prüft, ob der Pfad in VAR ein symbolischer Link ist (wie -h).

-k "$VAR"

Prüft, ob der Pfad in VAR die Berechtigung Sticky Bit hat.

-p "$VAR"

Prüft, ob der Pfad in VAR eine Pipe-Datei ist.

-r "$VAR"

Prüft, ob der Pfad in VAR für den aktuellen Benutzer lesbar ist.

-s "$VAR"

Prüft, ob der Pfad in VAR existiert und nicht leer ist.

-S "$VAR"

Prüft, ob der Pfad in VAR eine Socket-Datei ist.

-t "$VAR"

Prüft, ob der Pfad in VAR in einem Terminal geöffnet ist.

-u "$VAR"

Prüft, ob der Pfad in VAR die Berechtigung SUID gesetzt hat.

-w "$VAR"

Prüft, ob der Pfad in VAR für den aktuellen Benutzer schreibbar ist.

-x "$VAR"

Prüft, ob der Pfad in VAR für den aktuellen Benutzer ausführbar ist.

-O "$VAR"

Prüft, ob der Pfad in VAR dem aktuellen Benutzer gehört.

-G "$VAR"

Prüft, ob der Pfad in VAR zur betreffenden Gruppe des aktuellen Benutzers gehört.

-N "$VAR"

Prüft, ob der Pfad in VAR seit dem letzten Zugriff geändert wurde.

"$VAR1" -nt "$VAR2"

Prüft, ob der Pfad in VAR1 neuer ist als der Pfad in VAR2, entsprechend ihrer Änderungszeitpunkte.

"$VAR1" -ot "$VAR2"

Prüft, ob der Pfad in VAR1 älter ist als VAR2.

"$VAR1" -ef "$VAR2"

Dieser Ausdruck wird als wahr gewertet, wenn der Pfad in VAR1 ein Hardlink zu VAR2 ist.

Es empfiehlt sich, eine getestete Variable in Anführungszeichen zu setzen, da eine leere Variable einen Syntaxfehler für den Befehl test verursachen könnte. Die Testoptionen erfordern ein Operanden-Argument, und eine leere Variable ohne Anführungszeichen würde einen Fehler aufgrund eines fehlenden erforderlichen Arguments verursachen. Es gibt auch Tests für beliebige Textvariablen:

-z "$TXT"

Prüft, ob die Variable TXT leer ist (Größe Null).

-n "$TXT" oder test "$TXT"

Prüft, ob die Variable TXT nicht leer ist.

"$TXT1" = "$TXT2" oder "$TXT1" == "$TXT2"

Prüft, ob TXT1 und TXT2 gleich sind.

"$TXT1" != "$TXT2"

Prüft, ob TXT1 und TXT2 ungleich sind.

"$TXT1" < "$TXT2"

Prüft, ob TXT1 vor TXT2 kommt, in alphabetischer Reihenfolge.

"$TXT1" > "$TXT2"

Prüft, ob TXT1 nach TXT2 kommt, in alphabetischer Reihenfolge.

Unterschiedliche Sprachen können unterschiedliche Regeln für die alphabetische Reihenfolge haben. Für konsistente Ergebnisse, unabhängig von den Lokalisierungseinstellungen des Systems, auf dem das Skript ausgeführt wird, sollten Sie die Umgebungsvariable LANG auf C setzen (LANG=C), bevor Operationen durchgeführt werden, die eine alphabetische Sortierung umfassen. Diese Definition behält auch die Systemmeldungen in der Originalsprache bei, daher sollte sie nur innerhalb des Geltungsbereichs des Skripts genutzt werden.

Numerische Vergleiche haben ihren eigenen Satz an Testmöglichkeiten:

$NUM1 -lt $NUM2

Prüft, ob NUM1 kleiner ist als NUM2.

$NUM1 -gt $NUM2

Prüft, ob NUM1 größer ist als NUM2.

$NUM1 -le $NUM2

Prüft, ob NUM1 kleiner oder gleich NUM2 ist.

$NUM1 -ge $NUM2

Prüft, ob NUM1 größer oder gleich NUM2 ist.

$NUM1 -eq $NUM2

Prüft, ob NUM1 gleich NUM2 ist.

$NUM1 -ne $NUM2

Prüft, ob NUM1 ungleich NUM2 ist.

Alle Tests können die folgenden Modifikatoren erhalten:

! EXPR

Prüft, ob der Ausdruck EXPR falsch ist.

EXPR1 -a EXPR2

Prüft, ob sowohl EXPR1 als auch EXPR2 wahr sind.

EXPR1 -o EXPR2

Prüft, ob mindestens einer der beiden Ausdrücke wahr ist.

Ein weiteres bedingtes Konstrukt, case, ist eine Art Variante des if-Konstrukts. Die Anweisung case führt eine Liste von Befehlen aus, wenn ein angegebenes Element — zum Beispiel der Inhalt einer Variablen — in einer Liste von Elementen enthalten ist, die durch Pipes (den vertikalen Strich |) getrennt und durch ) abgeschlossen sind. Das folgende Beispielskript zeigt, wie Sie case nutzen, um das Softwarepaketierungsformat für eine bestimmte Linux-Distribution anzuzeigen:

#!/bin/bash

DISTRO=$1

echo -n "Distribution $DISTRO uses "
case "$DISTRO" in
	debian | ubuntu | mint)
    echo -n "the DEB"
  ;;
	centos | fedora | opensuse )
    echo -n "the RPM"
  ;;
	*)
    echo -n "an unknown"
  ;;
esac
echo " package format."

Sie müssen jede Liste von Mustern und zugehörigen Befehlen mit ;;, ;&, oder ;;& abschließen. Das letzte Muster, ein Sternchen, passt, wenn keines der vorherigen Muster dem Suchbegriff entspricht. Die Anweisung esac (case rückwärts) beendet das case-Konstrukt. Angenommen das vorangegangene Beispielskript heißt script.sh und wird mit opensuse als erstem Argument aufgerufen, so erzeugt es die folgende Ausgabe:

$ ./script.sh opensuse
Distribution opensuse uses the RPM package format.
Tip

Bash kennt die Option nocasematch, die den Mustervergleich ohne Berücksichtigung der Groß-/Kleinschreibung für das Konstrukt case und andere bedingte Befehle aktiviert. Der eingebaute Befehl shopt schaltet die Werte von Einstellungen um, die das optionale Verhalten der Shell steuern: shopt -s aktiviert (set) die angegebene Option und shopt -u deaktiviert (unset) die angegebene Option. Wenn Sie also shopt -s nocasematch vor das case-Konstrukt setzen, wird die Mustererkennung ohne Berücksichtigung der Groß-/Kleinschreibung aktiviert. Durch shopt geänderte Optionen wirken sich nur auf die aktuelle Sitzung aus, so dass geänderte Optionen in Skripten, die in einer Subshell ausgeführt werden – was der Standardweg ist, um ein Skript auszuführen –, keine Auswirkungen auf die Optionen der übergeordneten Sitzung haben.

Das gesuchte Element und die Muster werden einer Tildeerweiterung, Parametererweiterung, Befehlssubstitution und arithmetischen Erweiterung unterzogen. Wenn das gesuchte Element mit Anführungszeichen angegeben ist, werden diese entfernt, bevor der Abgleich erfolgt.

Schleifenkonstrukte

Skripte sind oft Werkzeuge zur Automatisierung sich wiederholender Aufgaben, wobei die gleiche Reihe von Befehlen ausgeführt wird, bis ein Stoppkriterium greift. Die Bash verfügt über drei Schleifenanweisungen – for, until und while –, die für verschiedene Schleifenkonstruktionen ausgelegt sind.

Das for-Konstrukt durchläuft eine gegebene Liste von Elementen — normalerweise eine Liste von Wörtern oder anderen, durch Leerzeichen getrennten Textsegmenten — und führt für jedes dieser Elemente denselben Befehlssatz aus. Vor jeder Iteration weist die for-Anweisung das aktuelle Element einer Variablen zu, die die Befehle dann nutzen können. Der Vorgang wird so lange wiederholt, bis keine Elemente mehr übrig sind. Die Syntax des for-Konstrukts lautet:

for VARNAME in LIST
do
	COMMANDS
done

VARNAME ist ein beliebiger Shellvariablenname und LIST eine beliebige Folge getrennter Begriffe. Die gültigen Begrenzungszeichen, die Elemente in der Liste trennen, werden durch die Umgebungsvariable IFS definiert; standardmäßig sind dies die Zeichen Space, Tab und Newline. Die Liste der auszuführenden Befehle steht zwischen den Anweisungen do und done, so dass Befehle so viele Zeilen wie nötig belegen können.

Im folgenden Beispiel nimmt der Befehl for jedes Element aus der angegebenen Liste — eine Folge von Zahlen — und weist es der Variablen NUM zu, ein Element nach dem anderen:

#!/bin/bash

for NUM in 1 1 2 3 5 8 13
do
	echo -n "$NUM is "
	if [ $(( $NUM % 2 )) -ne 0 ]
	then
		echo "odd."
	else
		echo "even."
  fi
done

Darüber hinaus enthält das Beispiel ein verschachteltes if-Konstrukt in Verbindung mit einem arithmetischen Ausdruck, um auszuwerten, ob die Zahl in der aktuellen Variable NUM gerade oder ungerade ist. Haben wir dieses Beispielskript script.sh genannt und es befindet sich im aktuellen Verzeichnis, so liefert es die folgende Ausgabe:

$ ./script.sh
1 is odd.
1 is odd.
2 is even.
3 is odd.
5 is odd.
8 is even.
13 is odd.

Die Bash unterstützt auch ein alternatives Format für for-Konstrukte, nämlich die Notation mit doppelten Klammern. Die Syntax ähnelt der for-Anweisung aus der Programmiersprache C und ist besonders nützlich bei der Arbeit mit Arrays:

#!/bin/bash

SEQ=( 1 1 2 3 5 8 13 )

for (( IDX = 0; IDX < ${#SEQ[*]}; IDX++ ))
do
	echo -n "${SEQ[$IDX]} is "
	if [ $(( ${SEQ[$IDX]} % 2 )) -ne 0 ]
	then
		echo "odd."
	else
		echo "even."
  fi
done

Dieses Beispielskript erzeugt dieselbe Ausgabe wie das vorangegangene. Statt jedoch in der Variable NUM jeweils ein Element zu speichern, nutzt es die Variable IDX, um den aktuellen Array-Index in aufsteigender Reihenfolge zu verfolgen — beginnend bei 0 und fortlaufend hinzfügend, solange er unter der Anzahl der Elemente im Array SEQ liegt. Das aktuelle Element wird über seine Array-Position mit ${SEQ[$IDX]} abgerufen.

Auf die gleiche Weise führt das until-Konstrukt eine Befehlssequenz aus, bis ein Testbefehl — wie der Befehl test selbst — mit Status 0 (Erfolg) endet. Die Schleifenstruktur aus dem vorherigen Beispiel können wir daher mit until wie folgt implementieren:

#!/bin/bash

SEQ=( 1 1 2 3 5 8 13 )

IDX=0

until [ $IDX -eq ${#SEQ[*]} ]
do
	echo -n "${SEQ[$IDX]} is "
	if [ $(( ${SEQ[$IDX]} % 2 )) -ne 0 ]
	then
		echo "odd."
	else
		echo "even."
  fi
  IDX=$(( $IDX + 1 ))
done

until-Konstrukte benötigen zwar mehr Anweisungen als for-Konstrukte, sind aber besser geeignet für nicht numerische Stoppkriterien, die Ausdrücke von test oder andere Befehle bereitstellen. Es ist wichtig, Aktionen einzubinden, die ein gültiges Stoppkriterium sicherstellen, wie z.B. das Inkrement einer Zählervariablen, da andernfalls eine Schleife unendlich laufen kann.

Die Anweisung while ist ähnlich der Anweisung until, aber while wiederholt die Befehlsreihe weiter, wenn der Testbefehl mit Status 0 (Erfolg) endet. Daher ist die Anweisung until [ $IDX -eq ${#SEQ[*]} ] aus dem vorherigen Beispiel äquivalent zu while [ $IDX -lt ${#SEQ[*]} ], da die Schleife wiederholt werden soll, solange der Array-Index kleiner als die Summe der Elemente im Array ist.

Ein ausführlicheres Beispiel

Nehmen wir an, ein Benutzer möchte regelmäßig eine Sammlung seiner Dateien und Verzeichnisse mit einem anderen Speichergerät synchronisieren, das an einem beliebigen Einhängepunkt im Dateisystem eingehängt ist — ein eigenes Backup-System soll aber nicht eingesetzt werden. Da es sich um eine regelmäßig durchzuführende Aktion handelt, bietet sich die Automatisierung mit einem Shellskript an.

Die Aufgabe ist einfach: Synchronisieren Sie jede Datei und jedes Verzeichnis von einem als erstes Argument des Skripts angegebenen Ursprungsverzeichnis zu einem als zweites Argument angegebenen Zielverzeichnis. Um das Hinzufügen oder Entfernen von Einträgen in der Liste zu erleichtern, liegt diese in einer separaten Datei namens ~/.sync.list mit einem Eintrag pro Zeile:

$ cat ~/.sync.list
Documents
To do
Work
Family Album
.config
.ssh
.bash_profile
.vimrc

Die Datei enthält eine Mischung aus Dateien und Verzeichnissen, einige mit Leerzeichen im Namen. Dies ist ein geeignetes Szenario für den eingebauten Bash-Befehl mapfile, der jeden gegebenen Textinhalt analysiert und daraus eine Array-Variable erstellt, wobei jede Zeile als einzelnes Array-Element platziert wird. Die Skriptdatei nennen wir sync.sh, und sie sieht wie folgt aus:

#!/bin/bash

set -ef

# List of items to sync
FILE=~/.sync.list

# Origin directory
FROM=$1

# Destination directory
TO=$2

# Check if both directories are valid
if [ ! -d "$FROM" -o ! -d "$TO" ]
then
  echo Usage:
  echo "$0 <SOURCEDIR> <DESTDIR>"
  exit 1
fi

# Create array from file
mapfile -t LIST < $FILE

# Sync items
for (( IDX = 0; IDX < ${#LIST[*]}; IDX++ ))
do
	echo -e "$FROM/${LIST[$IDX]} \u2192 $TO/${LIST[$IDX]}";
	rsync -qa --delete "$FROM/${LIST[$IDX]}" "$TO";
done

Die erste Aktion, die das Skript ausführt, ist die Definition von zwei Shellparametern mit dem Befehl set: Die Option -e beendet die Ausführung, sobald ein Befehl mit einem Status ungleich Null beendet wird, und die Option -f deaktiviert das Globbing von Dateinamen. Beide Optionen kürzen wir mit -ef ab. Dieser Schritt ist nicht zwingend, verringert aber die Wahrscheinlichkeit unerwarteten Verhaltens.

Bei den Anweisungen zur eigentlichen Anwendung der Skriptdatei können wir drei Teile unterscheiden:

  1. Skriptparameter sammeln und prüfen

    Die Variable FILE ist der Pfad zu der Datei mit der Liste der zu kopierenden Elemente: ~/.sync.list. Die Variablen FROM und TO sind der Ursprungs- bzw. Zielpfad. Da die letzten beiden Parameter vom Benutzer angegeben werden, durchlaufen sie einen einfachen Validierungstest, den das if-Konstrukt durchführt: Wenn einer der beiden kein gültiges Verzeichnis ist — geprüft durch [ ! -d "$FROM" -o ! -d "$TO" ] — zeigt das Skript eine kurze Hilfemeldung und beendet sich dann mit einem Exit-Status 1.

  2. Laden der Liste der Dateien und Verzeichnisse

    Sind alle Parameter definiert, erstellt der Befehl mapfile -t LIST < $FILE ein Array mit der Liste der zu kopierenden Elemente. Die Option -t von mapfile entfernt das abschließende Zeilenumbruchzeichen aus jeder Zeile, bevor sie in die Array-Variable LIST aufgenommen wird. Der Inhalt der durch die Variable FILE angegebenen Datei — ~/.sync.list — wird über die Eingabeumleitung gelesen.

  3. Kopiervorgang durchführen und Benutzer informieren

    Eine for-Schleife mit doppelter Klammerschreibweise durchläuft das Array der Elemente, wobei die Variable IDX die Index-Erhöhung festhält. Der Befehl echo informiert den Benutzer über jedes Element, das kopiert wird. Das escapete Unicodezeichen \u2192 für das Pfeil-nach-rechts-Zeichen steht in der Ausgabemeldung, weshalb die Option -e des Befehls echo gesetzt werden muss. Der Befehl rsync kopiert selektiv nur geänderte Dateien von der Quelle, weshalb er sich hier anbietet. Die rsync-Optionen -q und -a, zusammengefasst in -qa, verhindern Meldungen von rsync und aktivieren den Archivmodus, in dem alle Dateieigenschaften erhalten bleiben. Die Option --delete bewirkt, dass rsync ein Element im Ziel löscht, das in der Quelle nicht mehr existiert — nutzen Sie diese Option daher mit Vorsicht.

Sofern alle Elemente in der Liste im Homeverzeichnis des Benutzers carol (/home/carol) vorhanden sind und das Zielverzeichnis /media/carol/backup auf ein eingehängtes externes Speichergerät zeigt, erzeugt der Befehl sync.sh /home/carol /media/carol/backup die folgende Ausgabe:

$ sync.sh /home/carol /media/carol/backup
/home/carol/Documents → /media/carol/backup/Documents
/home/carol/"To do" → /media/carol/backup/"To do"
/home/carol/Work → /media/carol/backup/Work
/home/carol/"Family Album" → /media/carol/backup/"Family Album"
/home/carol/.config → /media/carol/backup/.config
/home/carol/.ssh → /media/carol/backup/.ssh
/home/carol/.bash_profile → /media/carol/backup/.bash_profile
/home/carol/.vimrc → /media/carol/backup/.vimrc

Das Beispiel geht davon aus, dass das Skript von root oder vom Benutzer carol ausgeführt wird, da die meisten Dateien für andere Benutzer nicht lesbar wären. Befindet sich script.sh nicht in einem der Verzeichnisse, die in der Umgebungsvariablen PATH aufgeführt sind, sollte es mit seinem vollständigen Pfad aufgerufen werden.

Geführte Übungen

  1. Wie setzen Sie den Befehl test ein, um zu überprüfen, ob der in der Variablen FROM gespeicherte Dateipfad neuer ist als eine Datei, deren Pfad in der Variablen TO gespeichert ist?

  2. Das folgende Skript soll eine Zahlenfolge von 0 bis 9 ausgeben, liefert stattdessen aber unendlich oft 0 aus. Was müssen Sie ändern, um die erwartete Ausgabe zu erhalten?

    #!/bin/bash
    
    COUNTER=0
    
    while [ $COUNTER -lt 10 ]
    do
      echo $COUNTER
    done
  3. Ein Benutzer hat ein Skript geschrieben, das eine sortierte Liste von Benutzernamen benötigt. Die resultierende sortierte Liste erscheint wie folgt:

    carol
    Dave
    emma
    Frank
    Grace
    henry

    Auf dem Rechner eines Kollegen ist die gleiche Liste jedoch wie folgt sortiert:

    Dave
    Frank
    Grace
    carol
    emma
    henry

    Wie sind die Unterschiede zwischen den beiden sortierten Listen zu erklären?

Offene Übungen

  1. Wie könnten Sie alle Befehlszeilenargumente des Skripts nutzen, um ein Bash-Array zu initialisieren?

  2. Warum wird der Befehl test 1 > 2 entgegen allen Erwartungen als wahr gewertet?

  3. Wie würden Sie das Standardfeldtrennzeichen vorübergehend nur auf das Zeilenumbruchzeichen ändern, so dass Sie es immer noch auf seinen ursprünglichen Inhalt zurücksetzen könnten?

Zusammenfassung

Diese Lektion beschreibt die für den Befehl test verfügbaren Tests und andere bedingte Schleifenkonstrukte, die zum Schreiben komplexerer Shellskripte erforderlich sind. Als Beispiel für eine praktische Shellskriptanwendung dient ein einfaches Dateisynchronisationsskript. Die Lektion behandelt folgende Schritte:

  • Erweiterte Tests für die if- und case-Bedingungskonstrukte.

  • Shellschleifenkonstrukte: for, until und while.

  • Iterieren durch Arrays und Parameter.

Die angesprochenen Befehle und Prozeduren sind:

test

Führt einen Vergleich zwischen den an den Befehl übergebenen Elementen durch.

if

Logisches Konstrukt, das in Skripten verwendet wird, um etwas als entweder wahr oder falsch zu bewerten und dann die Befehlsausführung basierend auf den Ergebnissen zu verzweigen.

case

Wertet mehrere Werte gegen eine einzelne Variable aus. Die Ausführung des Skriptbefehls erfolgt dann in Abhängigkeit vom Ergebnis des Befehls case.

for

Wiederholt die Ausführung eines Befehls auf der Grundlage eines bestimmten Kriteriums.

until

Wiederholt die Ausführung eines Befehls, bis ein Ausdruck als falsch gewertet wird.

while

Wiederholt die Ausführung eines Befehls, solange der gegebene Ausdruck als wahr gewertet wird.

Lösungen zu den geführten Übungen

  1. Wie setzen Sie den Befehl test ein, um zu überprüfen, ob der in der Variablen FROM gespeicherte Dateipfad neuer ist als eine Datei, deren Pfad in der Variablen TO gespeichert ist?

    Der Befehl test "$FROM" -nt "$TO" gibt einen Statuscode 0 zurück, wenn die Datei in der Variablen FROM neuer ist als die Datei in der Variablen TO.

  2. Das folgende Skript soll eine Zahlenfolge von 0 bis 9 ausgeben, liefert stattdessen aber unendlich oft 0 aus. Was müssen Sie ändern, um die erwartete Ausgabe zu erhalten?

    #!/bin/bash
    
    COUNTER=0
    
    while [ $COUNTER -lt 10 ]
    do
      echo $COUNTER
    done

    Die Variable COUNTER sollte inkrementiert werden, was mit dem arithmetischen Ausdruck COUNTER=$$COUNTER + 1 geschehen könnte, um schließlich das Stoppkriterium zu erreichen und die Schleife zu beenden.

  3. Ein Benutzer hat ein Skript geschrieben, das eine sortierte Liste von Benutzernamen benötigt. Die resultierende sortierte Liste erscheint wie folgt:

    carol
    Dave
    emma
    Frank
    Grace
    henry

    Auf dem Rechner eines Kollegen ist die gleiche Liste jedoch wie folgt sortiert:

    Dave
    Frank
    Grace
    carol
    emma
    henry

    Wie sind die Unterschiede zwischen den beiden sortierten Listen zu erklären?

    Die Sortierung basiert auf dem aktuellen Locale (Gebietsschemaparameter) des Systems. Um Inkonsistenzen zu vermeiden, setzen Sie die Sortierung mit der Umgebungsvariablen LANG auf C.

Lösungen zu den offenen Übungen

  1. Wie könnten Sie alle Befehlszeilenargumente des Skripts nutzen, um ein Bash-Array zu initialisieren?

    Die Befehle PARAMS=( $* ) oder PARAMS=( "$@" ) erzeugen ein Array namens PARAMS mit allen Argumenten.

  2. Warum wird der Befehl test 1 > 2 entgegen allen Erwartungen als wahr gewertet?

    Der Operator > ist für String-Tests vorgesehen, nicht für numerische Tests.

  3. Wie würden Sie das Standardfeldtrennzeichen vorübergehend nur auf das Zeilenumbruchzeichen ändern, so dass Sie es immer noch auf seinen ursprünglichen Inhalt zurücksetzen könnten?

    Sie können eine Kopie der Variablen IFS in einer anderen Variablen speichern: OLDIFS=$IFS. Dann definieren Sie den neuen Zeilentrenner mit IFS=$'\n' und setzen die Variable IFS mit IFS=$OLDIFS wieder zurück.

Linux Professional Insitute Inc. Alle Rechte vorbehalten. Besuchen Sie die LPI Learning Website: https://learning.lpi.org
Dieses Werk steht unter der Lizenz Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International.

Nächste Lektion

106.1 X11 installieren und konfigurieren (106.1 Lektion 1)

Nächste Lektion lesen

Linux Professional Insitute Inc. Alle Rechte vorbehalten. Besuchen Sie die LPI Learning Website: https://learning.lpi.org
Dieses Werk steht unter der Lizenz Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International.

LPI ist eine Non-Profit-Organisation.

© 2025 Linux Professional Institute (LPI) ist eine globale Organisation für Zertifizierungsstandards und zur Karriereplanung für Open-Source-Profis. Mit mehr als 250.000 Zertifikatsinhabern ist es die weltweit erste und größte herstellerneutrale Linux- und Open-Source-Zertifizierungsstelle. LPI verfügt über zertifizierte Fachleute in über 180 Ländern, bietet Prüfungen in mehreren Sprachen an und hat Hunderte von Trainingspartnern.

Unser Ziel ist es, wirtschaftliche und kreative Möglichkeiten für alle zu ermöglichen, indem wir Open-Source-Wissens- und Kompetenzzertifizierungen allgemein zugänglich machen.

  • LinkedIn
  • flogo-RGB-HEX-Blk-58 Facebook
  • Twitter
  • Kontaktieren Sie uns
  • Datenschutz und Cookie-Richtlinien

Haben Sie einen Fehler entdeckt oder möchten Sie helfen, diese Seite zu verbessern? Lassen Sie es uns wissen.

© 1999–2025 The Linux Professional Institute Inc. Alle Rechte vorbehalten.