105.2 Lektion 1
Zertifikat: |
LPIC-1 |
---|---|
Version: |
5.0 |
Thema: |
105 Shells und Shell-Skripte |
Lernziel: |
105.2 Einfache Skripte anpassen oder schreiben |
Lektion: |
1 von 2 |
Einführung
Die Shell-Umgebung unter Linux ermöglicht die Verwendung von Dateien – Skripte genannt –, die Befehle aus jedem im System verfügbaren Programm in Kombination mit in die Shell eingebauten Befehlen enthalten können, um benutzer- und/oder systembezogene Aufgaben zu automatisieren. Tatsächlich werden viele Wartungsaufgaben des Betriebssystems durch Skripte erledigt, die aus Befehlsfolgen, Entscheidungsstrukturen und bedingten Schleifen bestehen. Obwohl Skripte meist für Aufgaben im Zusammenhang mit dem Betriebssystem selbst gedacht sind, sind sie auch für benutzerorientierte Aufgaben nützlich, wie z.B. das massenhafte Umbenennen von Dateien, das Sammeln und Parsen von Daten oder andere sich wiederholende Aktivitäten auf der Kommandozeile.
Skripte sind nichts anderes als Textdateien, die sich wie Programme verhalten. Das eigentliche Programm — der Interpreter — liest und führt die in der Skriptdatei aufgeführten Anweisungen aus. Der Interpreter kann auch eine interaktive Sitzung starten, in der Befehle — einschließlich Skripte — gelesen und ausgeführt werden, wie es bei Linux-Shellsitzungen der Fall ist. Skriptdateien können diese Anweisungen und Befehle gruppieren, wenn diese zu komplex werden, um als Alias oder benutzerdefinierte Shellfunktion implementiert zu werden. Außerdem lassen sich Skriptdateien wie herkömmliche Programme warten — da es sich um Textdateien handelt, sind sie mit jedem einfachen Texteditor zu erstellen und zu bearbeiten.
Aufbau und Ausführung von Skripten
Grundsätzlich ist eine Skriptdatei eine geordnete Folge von Befehlen, die ein entsprechender Befehlsinterpreter ausführen muss. Art und Weise, wie ein Interpreter eine Skriptdatei liest, variieren. In einer Bash-Sitzung gibt es verschiedene Möglichkeiten, wobei der Standardinterpreter für eine Skriptdatei in der ersten Zeile des Skripts definiert wird, direkt nach den Zeichen #!
(bekannt als Shebang). In einem Skript mit Anweisungen für die Bash sollte die erste Zeile #!/bin/bash
lauten — damit ist /bin/bash
für alle Anweisungen in der Datei als Standardinterpreter definiert. Bis auf die erste Zeile werden alle anderen Zeilen, die mit dem Rautezeichen #
beginnen, ignoriert, so dass sie für Hinweise und Kommentare verwendet werden können. Leerzeilen werden ebenfalls ignoriert. Eine übliche Shell-Skriptdatei kann also wie folgt aussehen:
#!/bin/bash # A very simple script echo "Cheers from the script file! Current time is: " date +%H:%M
Dieses Skript hat nur zwei Anweisungen für den Interpreter /bin/bash
: den eingebauten Befehl echo
und den Befehl date
. Der einfachste Weg, eine Skriptdatei auszuführen, ist die Ausführung des Interpreters mit dem Skriptpfad als Argument. Angenommen das vorige Beispiel wurde in einer Skriptdatei namens script.sh
im aktuellen Verzeichnis gespeichert, dann wird es von der Bash mit dem folgenden Befehl gelesen und interpretiert:
$ bash script.sh Cheers from the script file! Current time is: 10:57
Der Befehl echo
fügt nach der Ausgabe des Inhalts automatisch eine neue Zeile ein — die Option -n
unterdrückt dieses Verhalten. echo -n
im Skript bewirkt also, dass die Ausgabe beider Befehle in derselben Zeile erscheint:
$ bash script.sh Cheers from the script file! Current time is: 10:57
Obwohl nicht erforderlich, hilft das Suffix .sh
, Shellskripte beim Auflisten und Suchen von Dateien zu identifizieren.
Tip
|
Die Bash ruft das Kommando, das nach dem |
Wenn die Skriptdatei von anderen Benutzern im System ausgeführt werden soll, müssen Sie die richtigen Leserechte setzen. Der Befehl chmod o+r script.sh
gibt allen Benutzern im System Leserechte, so dass sie script.sh
ausführen können, indem sie den Pfad zur Skriptdatei als Argument des Befehls bash
angeben. Alternativ setzen Sie für die Skriptdatei das Ausführungsbit, so dass die Datei wie ein herkömmlicher Befehl ausgeführt werden kann. Das Ausführungsbit setzen Sie für die Skriptdatei mit dem Befehl chmod
:
$ chmod +x script.sh
Ist das Ausführungsbit gesetzt, können Sie die Skriptdatei script.sh
im aktuellen Verzeichnis direkt mit dem Befehl ./script.sh
ausführen. Skripte, die sich in einem Verzeichnis befinden, das in der Umgebungsvariablen PATH
aufgeführt ist, sind auch ohne ihren vollständigen Pfad zugänglich.
Warning
|
Bei einem Skript mit sehr eingeschränkten Aufgaben kann die SUID-Berechtigung gesetzt sein, so dass auch normale Benutzer das Skript mit Root-Rechten ausführen können. In diesem Fall ist es sehr wichtig sicherzustellen, dass kein anderer Benutzer als root in die Datei schreiben darf. Andernfalls könnte ein normaler Benutzer die Datei ändern, um beliebige und potenziell schädliche Aktionen durchzuführen. |
Die Platzierung und Einrückung von Befehlen in Skriptdateien ist nicht allzu starr. Jede Zeile in einem Shellskript wird als gewöhnlicher Shellbefehl ausgeführt, und zwar in der Reihenfolge der Zeilen in der Skriptdatei, und dieselben Regeln, die für die Shelleingabeaufforderung gelten, gelten auch für jede einzelne Skriptzeile. Es ist möglich, zwei oder mehr Befehle in derselben Zeile zu platzieren, getrennt durch Semikola:
echo "Cheers from the script file! Current time is:" ; date +%H:%M
Obwohl dieses Format bequem scheint, ist es optional, da Sie aufeinanderfolgende Befehle in der Reihenfolge ihrer Ausführung auch in einzelne Zeilen schreiben können. Mit anderen Worten: Das Semikolon kann in Bash-Skriptdateien durch einen Zeilenumbruch ersetzt werden.
Bei der Ausführung eines Skripts werden die darin enthaltenen Befehle nicht direkt in der aktuellen Sitzung ausgeführt, sondern von einem neuen Bash-Prozess, der sogenannten Subshell. Das verhindert, dass das Skript die Umgebungsvariablen der aktuellen Sitzung überschreibt und unkontrollierte Änderungen in der aktuellen Sitzung hinterlässt. Wollen Sie den Inhalt des Skripts in der aktuellen Shellsitzung ausführen, führen Sie es mit source script.sh
oder . script.sh
aus (beachten Sie das Leerzeichen zwischen dem Punkt und dem Skriptnamen).
Wie bei der Ausführung jedes anderen Befehls ist der Shellprompt erst wieder verfügbar, wenn das Skript seine Ausführung beendet und sein Exitstatuscode in der Variablen $?
verfügbar ist. Um dieses Verhalten so zu ändern, dass die aktuelle Shell auch endet, sobald das Skript endet, stellen Sie dem Skript — oder jedem anderen Befehl — den Befehl exec
voran. Er ersetzt auch den Exitstatuscode der aktuellen Shellsitzung durch seinen eigenen.
Variablen
Variablen in Shellskripten verhalten sich genauso wie in interaktiven Sitzungen, vorausgesetzt der Interpreter ist derselbe. Zum Beispiel weist SOLUTION=42
(ohne Leerzeichen um das Gleichheitszeichen) der Variable namens SOLUTION
den Wert 42
zu. Per Konvention werden für Variablennamen Großbuchstaben verwendet, dies ist jedoch nicht zwingend. Variablennamen sollten jedoch mit einem Buchstaben beginnen.
Neben den gewöhnlichen, vom Benutzer erstellten Variablen haben Bashskripte auch einen Satz spezieller Variablen, die Parameter genannt werden. Im Gegensatz zu gewöhnlichen Variablen beginnen Parameternamen mit einem nicht alphabetischen Zeichen, das ihre Funktion bezeichnet. Argumente, die an ein Skript übergeben werden, und andere nützliche Informationen werden in Parametern wie $0
, $*
, $?
usw. gespeichert, wobei das Zeichen nach dem Dollarzeichen die erhaltene Information angibt:
$*
-
Alle Argumente, die an das Skript übergeben wurden.
$@
-
Alle Argumente, die an das Skript übergeben wurden. Bei Verwendung mit doppelten Anführungszeichen, wie in
"$@"
, wird jedes Argument in doppelte Anführungszeichen gesetzt. $#
-
Die Anzahl der Argumente.
$0
-
Der Name der Skriptdatei.
$!
-
PID des zuletzt ausgeführten Programms.
$$
-
PID der aktuellen Shell.
$?
-
Numerischer Exitstatuscode des letzten beendeten Befehls. Für POSIX-Standardprozesse bedeutet ein numerischer Wert von
0
, dass das letzte Kommando erfolgreich ausgeführt wurde, was auch für Shellskripte gilt.
Ein Positionsparameter ist ein Parameter, der durch eine oder mehrere Ziffern bezeichnet wird, die nicht die Ziffer 0
sind. Zum Beispiel entspricht $1
dem ersten Argument, das dem Skript übergeben wurde (Positionsparameter eins), $2
entspricht dem zweiten Argument und so weiter. Wenn die Position eines Parameters größer als neun ist, muss er mit geschweiften Klammern referenziert werden, wie in ${10}
, ${11}
usw.
Gewöhnliche Variablen hingegen sind dazu gedacht, manuell eingegebene Werte oder die von anderen Befehlen erzeugte Ausgabe zu speichern. Der Befehl read
kann z.B. innerhalb des Skripts verwendet werden, um den Benutzer während der Skriptausführung nach Eingaben zu fragen:
echo "Do you want to continue (y/n)?" read ANSWER
Der zurückgegebene Wert wird in der Variablen ANSWER
gespeichert. Ist der Name der Variablen nicht angegeben, wird standardmäßig der Variablenname REPLY
verwendet. Sie können den Befehl read
auch verwenden, um mehr als eine Variable gleichzeitig zu lesen:
echo "Type your first name and last name:" read NAME SURNAME
In diesem Fall wird jeder durch Leerzeichen getrennte Begriff den Variablen NAME
bzw. SURNAME
zugewiesen. Wenn die Anzahl der angegebenen Begriffe größer ist als die Anzahl der Variablen, werden die überzähligen Begriffe in der letzten Variablen gespeichert. Mit der Option -p
gibt read
selbst die Meldung an den Benutzer aus, wodurch der Befehl echo
überflüssig wird:
read -p "Type your first name and last name:" NAME SURNAME
Skripte, die Systemaufgaben ausführen, benötigen oft Informationen, die von anderen Programmen bereitgestellt werden. Die Backtick-Notation dient dazu, die Ausgabe eines Befehls in einer Variablen zu speichern:
$ OS=`uname -o`
Im Beispiel wird die Ausgabe des Befehls uname -o
in der Variablen OS
gespeichert. Ein identisches Ergebnis liefert $()
:
$ OS=$(uname -o)
Die Länge einer Variablen, d.h. die Anzahl der Zeichen, die sie enthält, wird durch Voranstellen einer Raute #
vor dem Namen der Variablen zurückgegeben. Diese Funktion erfordert jedoch die Syntax der geschweiften Klammern, um die Variable anzugeben:
$ OS=$(uname -o) $ echo $OS GNU/Linux $ echo ${#OS} 9
Die Bash verfügt auch über eindimensionale Array-Variablen, um eine Menge zusammengehöriger Elemente mit einem einzigen Variablennamen zu speichern. Jedes Element in einem Array hat einen numerischen Index, über den das Schreiben und Lesen von Werten im entsprechenden Element erfolgt. Im Gegensatz zu gewöhnlichen Variablen müssen Sie Arrays mit dem in der Bash fest eingebauten Befehl declare
deklarieren. Um etwa eine Variable mit dem Namen SIZES
als Array zu deklarieren:
$ declare -a SIZES
Sie können Arrays auch implizit deklarieren, wenn sie aus einer vordefinierten Liste von Elementen unter Verwendung der Klammerschreibweise gefüllt werden:
$ SIZES=( 1048576 1073741824 )
Im Beispiel haben wir die beiden großen Integerwerte im Array SIZES
gespeichert. Array-Elemente müssen wir mit geschweiften Klammern und eckigen Klammern referenzieren, andernfalls wird Bash das Element nicht korrekt ändern oder anzeigen. Da Array-Indizes bei 0 beginnen, befindet sich der Inhalt des ersten Elements in ${SIZES[0]}
, das zweite Element in ${SIZES[1]}
und so weiter:
$ echo ${SIZES[0]} 1048576 $ echo ${SIZES[1]} 1073741824
Im Gegensatz zum Lesen erfolgt das Ändern des Inhalts eines Array-Elements ohne die geschweiften Klammern (z.B. SIZES[0]=1048576
). Wie bei normalen Variablen wird die Länge eines Elements in einem Array mit dem Hashzeichen zurückgegeben (z.B. ${#SIZES[0]}
für die Länge des ersten Elements im Array SIZES
). Mit @
oder *
als Index wird die Gesamtzahl der Elemente in einem Array zurückgegeben:
$ echo ${#SIZES[@]} 2 $ echo ${#SIZES[*]} 2
Sie können Arrays auch unter Verwendung einer Befehlsausgabe als Anfangselemente durch Befehlssubstitution deklarieren. Das folgende Beispiel zeigt, wie Sie ein Bash-Array erstellen, dessen Elemente die unterstützten Dateisysteme des aktuellen Systems sind:
$ FS=( $(cut -f 2 < /proc/filesystems) )
Der Befehl cut -f 2 < /proc/filesystems
zeigt alle Dateisysteme an, die der laufende Kernel derzeit unterstützt (wie in der zweiten Spalte der Datei /proc/filesystems
aufgelistet), so dass das Array FS
nun ein Element für jedes unterstützte Dateisystem enthält. Jeder Textinhalt kann zur Initialisierung eines Arrays dienen, da standardmäßig alle Begriffe, die durch Leerzeichen, Tab oder Neuzeilen begrenzt sind, zu einem Array-Element werden.
Tip
|
Die Bash behandelt jedes Zeichen der Umgebungsvariablen |
Arithmetische Ausdrücke
Die Bash bietet mit dem eingebauten Befehl expr
eine praktische Methode, um ganzzahlige arithmetische Operationen durchzuführen. Sie addieren zwei numerische Variablen, zum Beispiel $VAL1
und $VAL2
, mit dem folgenden Befehl:
$ SUM=`expr $VAL1 + $VAL2`
Der resultierende Wert des Beispiels ist dann in der Variablen $SUM
verfügbar. Sie können den Befehl expr
durch $(())
ersetzen, und das Beispiel in SUM=$(( $VAL1 + $VAL2 ))
umschreiben. Potenzausdrücke sind mit dem Doppelsternchenoperator ebenfalls erlaubt, so dass die vorherige Arraydeklaration SIZES=( 1048576 1073741824)
in SIZES=( $((1024**2)) $((1024**3)) )
umgeschrieben werden kann.
Befehlssubstitution ist auch in arithmetischen Ausdrücken möglich. Zum Beispiel enthält die Datei /proc/meminfo
detaillierte Informationen über den Systemspeicher, einschließlich der Anzahl der freien Bytes im RAM:
$ FREE=$(( 1000 * `sed -nre '2s/[^[:digit:]]//gp' < /proc/meminfo` ))
Das Beispiel zeigt, wie Sie den Befehl sed
verwenden, um den Inhalt von /proc/meminfo
innerhalb des arithmetischen Ausdrucks zu analysieren. Die zweite Zeile der Datei /proc/meminfo
enthält die Menge des freien Speichers in Tausenden Bytes, so dass der arithmetische Ausdruck diese mit 1000 multipliziert, um die Anzahl der freien Bytes im RAM zu erhalten.
Bedingte Ausführung
Manche Skripte sollen nicht alle Befehle in der Skriptdatei ausführen, sondern nur jene, die definierte Kriterien erfüllen. Ein Wartungsskript soll z.B. nur dann eine Warnmeldung per E-Mail an den Administrator senden, wenn die Ausführung eines Befehls fehlschlägt. Die Bash bietet spezielle Methoden, um den Erfolg der Befehlsausführung zu bewerten, sowie allgemeine bedingte Strukturen, die denen in gängigen Programmiersprachen ähneln.
Durch die Trennung von Befehlen mit &&
wird der rechts stehende Befehl nur dann ausgeführt, wenn der links stehende nicht auf einen Fehler gestoßen ist, d.h. wenn sein Exitstatus gleich 0
war:
COMMAND A && COMMAND B && COMMAND C
Das umgekehrte Verhalten gilt, wenn Befehle mit ||
getrennt werden: In diesem Fall wird der nachfolgende Befehl nur ausgeführt, wenn der vorherige auf einen Fehler gestoßen ist, d.h. wenn sein zurückgebender Statuscode von 0
verschieden ist.
Eines der wichtigsten Konzepte aller Programmiersprachen ist die Möglichkeit, Befehle in Abhängigkeit von vorher definierten Bedingungen auszuführen. Die einfachste Art, Befehle bedingt auszuführen, ist der in der Bash eingebaute Befehl if
, der einen oder mehrere Befehle nur dann ausführt, wenn der als Argument angegebene Befehl den Statuscode 0
(Erfolg) zurückgibt. Ein weiterer Befehl, test
, dient der Überprüfung verschiedener spezieller Kriterien, weshalb er meist in Verbindung mit if
eingesetzt wird. Im folgenden Beispiel erscheint die Meldung Confirmed: /bin/bash is executable.
nur, wenn die Datei /bin/bash
existiert und ausführbar ist:
if test -x /bin/bash ; then echo "Confirmed: /bin/bash is executable." fi
Die Option -x
bewirkt, dass der Befehl test
nur dann den Statuscode 0
zurückgibt, wenn der angegebene Pfad eine ausführbare Datei ist. Das folgende Beispiel zeigt eine andere Möglichkeit, dasselbe Ergebnis zu erzielen, da die eckigen Klammern das test
ersetzen können:
if [ -x /bin/bash ] ; then echo "Confirmed: /bin/bash is executable." fi
Die Anweisung else
ist optional in der if
-Struktur — falls vorhanden, definiert sie einen Befehl oder eine Befehlsfolge, die ausgeführt werden soll, wenn der bedingte Ausdruck nicht wahr ist:
if [ -x /bin/bash ] ; then echo "Confirmed: /bin/bash is executable." else echo "No, /bin/bash is not executable." fi
if
-Strukturen müssen immer mit fi
enden, damit der Bash-Interpreter weiß, wo ein bedingter Befehl endet.
Ausgabe eines Skripts
Auch wenn ein Skript nur dateibezogene Operationen umfasst, ist es wichtig, Fortschrittsmeldungen in der Standardausgabe anzuzeigen, damit der Benutzer über Probleme informiert ist und diese Meldungen beispielsweise zur Erstellung von Logs nutzen kann.
Der in der Bash eingebaute Befehl echo
dient üblicherweise der Anzeige einfacher Text-Strings, bietet aber auch einige erweiterte Funktionen: Mit der Option -e
ist der Befehl echo
in der Lage, Sonderzeichen mit Hilfe von Escapesequenzen (eine Backslashsequenz, die ein Sonderzeichen markiert) anzuzeigen, zum Beispiel:
#!/bin/bash # Get the operating system's generic name OS=$(uname -o) # Get the amount of free memory in bytes FREE=$(( 1000 * `sed -nre '2s/[^[:digit:]]//gp' < /proc/meminfo` )) echo -e "Operating system:\t$OS" echo -e "Unallocated RAM:\t$(( $FREE / 1024**2 )) MB"
Während die Anführungszeichen bei echo
ohne Optionen optional sind, müssen sie bei der Option -e
gesetzt sein, da sonst die Sonderzeichen möglicherweise nicht korrekt dargestellt werden. Im vorangegangenen Skript nutzen beide echo
-Befehle das Tabulatorzeichen \t
, um den Text einzurücken, was zu der folgenden Ausgabe führt:
Operating system: GNU/Linux Unallocated RAM: 1491 MB
Sie können auch das Zeilenumbruchzeichen \n
zum Trennen der Ausgabezeilen nutzen, so dass dieselbe Ausgabe erscheint, wenn die beiden echo
-Befehle zu einem einzigen zusammengefasst werden:
echo -e "Operating system:\t$OS\nUnallocated RAM:\t$(( $FREE / 1024**2 )) MB"
Obwohl der Befehl echo
für die Anzeige der meisten Textnachrichten geeignet ist, empfiehlt er sich nicht für spezifischere Textmuster. Der in der Bash eingebaute Befehl printf
gibt Ihnen mehr Kontrolle über die Anzeige der Variablen. printf
verwendet das erste Argument als Format für die Ausgabe, wobei Platzhalter durch die folgenden Argumente in der Reihenfolge ersetzt werden, in der sie in der Befehlszeile erscheinen. Sie können die Meldung des vorherigen Beispiels daher auch mit dem folgenden printf
-Befehl erzeugen:
printf "Operating system:\t%s\nUnallocated RAM:\t%d MB\n" $OS $(( $FREE / 1024**2 ))
Der Platzhalter %s
ist für Textinhalte gedacht (er wird durch die Variable $OS
ersetzt) und der Platzhalter %d
für ganzzahlige Werte (er wird durch die resultierende Anzahl der freien Megabytes im RAM ersetzt). printf
hängt kein Zeilenumbruchzeichen am Ende des Textes an, daher sollten Sie \n
bei Bedarf an das Ende des Musters setzen. Das gesamte Muster soll als ein einziges Argument interpretiert werden, daher schließen wir es in Anführungszeichen ein.
Tip
|
Sie können das Format der von |
Bei printf
werden die Variablen außerhalb des Textmusters platziert, so dass sie es in einer separaten Variablen speichern können:
MSG='Operating system:\t%s\nUnallocated RAM:\t%d MB\n' printf "$MSG" $OS $(( $FREE / 1024**2 ))
Diese Methode ist besonders nützlich, um je nach Anforderungen des Benutzers verschiedene Ausgabeformate anzuzeigen. So ist es z.B. einfacher, ein Skript zu schreiben, das ein bestimmtes Textmuster verwendet, wenn der Benutzer eine CSV-Liste (Comma Separated Values) anstelle einer Standardausgabemeldung benötigt.
Geführte Übungen
-
Die Option
-s
für den Befehlread
empfiehlt sich für die Eingabe von Passwörtern, da der getippte Inhalt nicht auf dem Bildschirm erscheint. Wie setzen Sie den Befehlread
ein, um die Eingaben des Benutzers in der VariablenPASSWORD
zu speichern und gleichzeitig den eingetippten Inhalt zu verbergen? -
Der einzige Zweck des Befehls
whoami
ist es, den Benutzernamen des Benutzers anzuzeigen, der ihn aufgerufen hat. Daher wird er meist in Skripten verwendet, um den ausführenden Benutzer zu identifizieren. Wie können Sie in einem Bash-Skript die Ausgabe des Befehlswhoami
in der VariablenWHO
speichern? -
Welcher Bash-Operator sollte zwischen den Befehlen
apt-get dist-upgrade
undsystemctl reboot
stehen, wenn der Root-Benutzersystemctl reboot
nur in dem Fall ausführen möchte, dassapt-get dist-upgrade
erfolgreich beendet wurde?
Offene Übungen
-
Nach dem Versuch, ein neu erstelltes Bash-Skript auszuführen, erhalten Sie die folgende Fehlermeldung:
bash: ./script.sh: Permission denied
Sie selbst haben die Datei
./script.sh
erstellt. Welche ist die wahrscheinliche Ursache für diesen Fehler? -
Eine Skriptdatei namens
do.sh
ist ausführbar, und der symbolische Linkundo.sh
zeigt darauf. Wie könnten Sie aus dem Skript heraus erkennen, ob der aufrufende Dateinamedo.sh
oderundo.sh
lautet? -
In einem System mit einem ordnungsgemäß konfigurierten E-Mail-Dienst sendet der Befehl
mail -s "Maintenance Error" root <<<"Scheduled task error"
die Benachrichtigung per E-Mail an den Benutzer root. Ein solcher Befehl könnte in unbeaufsichtigten Tasks (z.B. cronjobs) dazu dienen, den Systemadministrator über ein unerwartetes Problem zu informieren. Schreiben Sie eine if-Anweisung, die denmail
-Befehl ausführt, falls der Exit-Status des vorherigen Befehls — wie immer dieser lautet — nicht erfolgreich ist.
Zusammenfassung
Diese Lektion behandelt die grundlegenden Konzepte zum Verstehen und Schreiben von Bash-Shellskripten. Shellskripte sind ein zentraler Bestandteil jeder Linux-Distribution, da sie eine sehr flexible Möglichkeit zur Automatisierung von Benutzer- und Systemaufgaben bieten, die in der Shellumgebung ausgeführt werden. Die Lektion führt durch die folgenden Schritte:
-
Shellskriptstruktur und korrekte Skriptdateiberechtigungen
-
Skriptparameter
-
Verwendung von Variablen zum Lesen von Benutzereingaben und zum Speichern der Ausgabe von Befehlen
-
Bash-Arrays
-
Einfache Tests und bedingte Ausführung
-
Ausgabeformatierung
Die angesprochenen Befehle und Prozeduren sind:
-
Bash-Builtin-Notation für Befehlssubstitution, Array-Erweiterung und arithmetische Ausdrücke
-
Bedingte Befehlsausführung mit den Operatoren
||
und&&
-
echo
-
chmod
-
exec
-
read
-
declare
-
test
-
if
-
printf
Lösungen zu den geführten Übungen
-
Die Option
-s
für den Befehlread
empfiehlt sich für die Eingabe von Passwörtern, da der getippte Inhalt nicht auf dem Bildschirm erscheint. Wie setzen Sie den Befehlread
ein, um die Eingaben des Benutzers in der VariablenPASSWORD
zu speichern und gleichzeitig den eingetippten Inhalt zu verbergen?read -s PASSWORD
-
Der einzige Zweck des Befehls
whoami
ist es, den Benutzernamen des Benutzers anzuzeigen, der ihn aufgerufen hat. Daher wird er meist in Skripten verwendet, um den ausführenden Benutzer zu identifizieren. Wie können Sie in einem Bash-Skript die Ausgabe des Befehlswhoami
in der VariablenWHO
speichern?WHO=`whoami`
oder
WHO=$(whoami)
-
Welcher Bash-Operator sollte zwischen den Befehlen
apt-get dist-upgrade
undsystemctl reboot
stehen, wenn der Root-Benutzersystemctl reboot
nur in dem Fall ausführen möchte, dassapt-get dist-upgrade
erfolgreich beendet wurde?Der Operator
&&
, wie inapt-get dist-upgrade && systemctl reboot
.
Lösungen zu den offenen Übungen
-
Nach dem Versuch, ein neu erstelltes Bash-Skript auszuführen, erhalten Sie die folgende Fehlermeldung:
bash: ./script.sh: Permission denied
Sie selbst haben die Datei
./script.sh
erstellt. Welche ist die wahrscheinliche Ursache für diesen Fehler?Für die Datei
./script.sh
ist die Ausführungsberechtigung nicht gesetzt. -
Eine Skriptdatei namens
do.sh
ist ausführbar, und der symbolische Linkundo.sh
zeigt darauf. Wie könnten Sie aus dem Skript heraus erkennen, ob der aufrufende Dateinamedo.sh
oderundo.sh
lautet?Die spezielle Variable
$0
enthält den Dateinamen, mit dem das Skript aufgerufen wurde. -
In einem System mit einem ordnungsgemäß konfigurierten E-Mail-Dienst sendet der Befehl
mail -s "Maintenance Error" root <<<"Scheduled task error"
die Benachrichtigung per E-Mail an den Benutzer root. Ein solcher Befehl könnte in unbeaufsichtigten Tasks (z.B. cronjobs) dazu dienen, den Systemadministrator über ein unerwartetes Problem zu informieren. Schreiben Sie eine if-Anweisung, die denmail
-Befehl ausführt, falls der Exit-Status des vorherigen Befehls — wie immer dieser lautet — nicht erfolgreich ist.if [ "$?" -ne 0 ]; then mail -s "Maintenance Error" root <<<"Scheduled task error"; fi