Mit der Shell kann man auch programmieren. Ein sogenanntes Shellscript besteht einfach aus einer Textdatei, in der entsprechende Kommandos aufgeführt sind. Ruft man nun die Textdatei auf wie ein Programm, werden die Kommandos darin nacheinander abgearbeitet. Der Vorteil liegt auf der Hand: Oft benutzte Arbeitsabläufe lassen sich durch die Eingabe eines einzelnen Kommandos durchführen.

Mit Shellscripten ist aber noch mehr möglich: Die Shell kennt auch Bedingungen und Verzweigungen (also if-Anweisungen) und ebenso Schleifen, wie in einer richtigen Programmiersprache. Einem Shellscript lassen sich auch Parameter übergeben. Dadurch werden Shellscripte sehr flexibel. Ich möchte hier keinen kompletten Kurs in Shellscriptprogrammierung geben, was den Rahmen dieser Seite sprengen würde, aber ich möchte dem Einsteiger ein paar Möglichkeiten aufzeigen.

Ein einfaches Shellscript

Beginnen wir mit einem ganz einfachen Script ohne irgendwelche Verzweigungen und Parameter. Zuerst öffnen wir mit dem Editor unserer Wahl ein neues Dokument und schreiben in die erste Zeile:

#!/bin/bash

Diese Zeile wird auch Shebang genannt und definiert mit welchem Kommandointerpreter (also welcher Shell) das Script ausgewertet werden soll. Wenn wir das Script auch für andere Unix-Systeme (wie z. B. BSD) kompatibel halten wollen, können wir auch statt /bin/bash, /bin/sh schreiben. Allerdings bietet die Bash einige Funktionen an, die die Sh nicht kennt. Diese dürfen dann natürlich auch nicht verwendet werden.

Grundsätzlich gilt: Zeilen die mit # beginnen sind Kommentarzeilen und werden nicht ausgewertet. Die erste Zeile mit dem Shebang ist dabei eine Ausnahme. Zur Übersichtlichkeit können Sie außerdem an beliebigen Stellen Leerzeilen einfügen.

Welche Aufgabe soll nun unser Script vollbringen? Nehmen wir an, wir möchten einige wichtige Dateien und Konfigurationseinstellungen auf einen USB-Stick speichern. Hierzu muß also der USB-Stick zuerst als Laufwerk gemountet werden und dann die Dateien in gepackter Form auf ihn gesichert werden. Am Ende sollte noch die Meldung erscheinen, das die Sicherung durchgeführt wurde, und der USB-Stick sollte wieder ausgehängt werden.

Das mounten eines Laufwerks geschieht durch den Befehl mount. Normalerweise kann mount nur vom Systembenutzer Root benutzt werden. Jedoch kann für jedes Laufwerk (und dies macht besonders bei Wechselmedien, wie etwa CD, Zip oder eben einem USB-Stick, Sinn) bestimmt werden, das dieses Laufwerk durch einen beliebigen Benutzer gemountet und ungemountet werden darf. Alle Einstellungen bezüglich der Laufwerke werden in der Datei /etc/fstab gemacht.

Wie nehmen hier einfach an, daß der USB-Stick durch Root bereits entsprechend in der /etc/fstab konfiguriert wurde. Weiterhin nehmen wir an, das der Mountpoint des USB-Sticks mit /mnt/usbstick definiert wurde. Also ergänzen wir unser Script folgendermaßen:

#!/bin/bash

# Sicherungsscript

mount /mnt/usbstick

Nun wollen wir aber nicht unser ganzes Homeverzeichnis sichern, sondern nur das was für uns wirklich relevant ist (da sonst der Platz nicht reichen würde). Da wir mit KDE arbeiten, wollen wir die KDE-Einstellungen, inklusive der Einstellungen und Daten von KDE-Programmen wie etwa Kmail und Korganizer sichern. Diese Daten liegen alle im versteckten Unterordner .kde in unserem Homeverzeichnis. Außerdem möchten wir noch alle unsere Mails in Kmail sichern. Diese liegen im Ordner Mail. Desweiteren benutzen wir den Webbrowser Mozilla und möchten alle Boomarks und sonstige Einstellungen sichern. Diese liegen im Ordner .mozilla. Als letztes haben wir noch den Ordner Documents, in dem wir alle unsere Office-Dateien, wie etwa Briefe und Tabellendokumente abgespeichert haben. All diese müssen nun mit tar auf den USB-Stick gepackt werden:

#!/bin/bash

# Sicherungsscript

mount /mnt/usbstick

cd ~

tar czf /mnt/usbstick/kde.tar.gz .kde
tar czf /mnt/usbstick/Mail.tar.gz Mail
tar czf /mnt/usbstick/mozilla.tar.gz .mozilla
tar czf /mnt/usbstick/Documents.tar.gz Documents

Jetzt fehlt nur noch das unmounten des USB-Sticks und die Bestätigungsmeldung:

#!/bin/bash

# Sicherungsscript

mount /mnt/usbstick

cd ~

tar czf /mnt/usbstick/kde.tar.gz .kde
tar czf /mnt/usbstick/Mail.tar.gz Mail
tar czf /mnt/usbstick/mozilla.tar.gz .mozilla
tar czf /mnt/usbstick/Documents.tar.gz Documents

umount /mnt/usbstick

echo "Sicherung wurde durchgeführt"

exit 0

Der Befehl echo gibt eine beliebige Zeichenkette auf der Standardausgabe (im Normalfall also dem Bildschirm) aus. Beendet wird das Script mit exit 0. Jedes Programm auf einem Linuxsystem, soll bei seiner Beendigung einen Exitstatus zurückliefern. Der Exitstatus 0 ist dabei eine normale Beendigung ohne Fehler. Bei einem Exitstatus ungleich 0 ist ein Fehler aufgetreten. Mit exit 0 liefert das Script den Exitstatus 0 zurück und beendet sich.

Falls wir dies nicht schon getan haben, können wir der Datei nun einen Namen geben, mit dem wir das Script auch später aufrufen. Wenn wir das Script in einen Ordner speichern, der in der Umgebungsvariable "PATH" enthalten ist, müssen wir später nicht den vollständigen Pfad zum Script angeben. Welche Ordner alle in "PATH" enthalten sind können Sie so sehen:

rene@thor:~> echo $PATH
/home/rene/bin:/usr/local/bin:/usr/bin:/usr/X11R6/bin:/bin:/usr/games:/opt/gnome/bin:/opt/kde3/bin:/usr/lib/java/jre/bin:/usr/NX/bin:.:~/bin:/usr/lib/SunJava2-1.4.1/bin:~/bin:/usr/lib/SunJava2-1.4.1/bin:~/bin:/usr/lib/SunJava2-1.4.1/bin

Sie können PATH selber ergänzen, indem Sie beispielsweise Ihre .bashrc ergänzen:

export PATH=$PATH':~/bin'

Diese Zeile ergänzt die bisherigen Pfade in PATH mit dem zusätzlichen Ordner bin in Ihrem Homeverzeichnis.

Versuchen wir nun unser Script zu starten. Falls Ihr Script nun in einem Ordner liegt, der in PATH vorkommt, können Sie es durch die Eingabe des Namens starten, falls nicht müssen Sie den Pfad angeben. Falls Sie sich im gleichen Ordner wie das Script befinden, können sie auch "./" vor den Dateinamen setzen. Doch was passiert? Sie bekommen eine Fehlermeldung, das Sie keine Berechtigung haben? Richtig! Sie erinnern sich vielleicht, das alle angelegten Dateien für den Besitzer zwar Les- und Schreibbar sind, aber nicht ausführbar. Wir müssen die Datei also erst ausführbar machen:

rene@thor:~> chmod 755 mein_script

Sie können statt 755 (wodurch das Script für alle ausführbar wird) auch durchaus 744 angeben. Nur für Sie selbst als den Besitzer des Scripts sollte es schon ausführbar sein. Wenn Sie es jetzt noch mal versuchen läuft es. So einfach ist das.

Parameter

Parameter sind zusätzliche Angaben die Sie dem Script beim aufrufen mitgeben. Innerhalb eines Scripts können Sie auf diese Angaben zugreifen.

Nehmen wir an Sie wollen obiges Sicherungsscript so erweitern, das Sie den Sicherungen jeweils nach belieben einen anderen Dateinamen geben können. Auf diese Weise können Sie z. B. das Datum im Dateinamen mit angeben. Die Sicherungen überschreiben dann jeweils die alten nicht, sondern Sie können mehrere Sicherungen parallel zueinander behalten.

Es gibt einige Variablen die alle Informationen zum Script beinhalten. Eine Variable beginnt immer mit dem $-Zeichen. Die Variable $# enthält die Anzahl der dem Script übergebenen Parameter. Der Name des Scripts selber steht in der Variable $0. Die Variablen $1 - $9 enthalten den ersten bis neunten Parameter.

Nehmen wir an ich habe mein Script "sichere" genannt. Wenn ich es nun mit sichere irgendwas aufrufe, enthält die Variable $# den Wert 1, die Variable $0 den Wert "sichere" und die Variable $1 den Wert "irgendwas".

Ein kleines Testscript soll dies verdeutlichen:

#!/bin/bash

# ein kleiner Variablentest

echo "\$# = $#"
echo "\$0 = $0"
echo "\$1 = $1"

exit 0

Ich speichere dieses Script nun unter dem Namen test, mache es ausführbar und starte es:

rene@thor:~> chmod 744 test
rene@thor:~> ./test bla
$# = 1
$0 = ./test
$1 = bla

Auf Variablen reagiert die echo-Anweisung so, das sie mit Ihrem Wert ausgegeben werden. Der Backslash vor dem $-Zeichen schützt sie jedoch davor. Der Backslash dient als "Entwerter", der dem $-Zeichen seine besondere Bedeutung nimmt.

Für unser Script muß jedoch noch etwas bedacht werden: Wir wollen eine Variable mitten in einen vorhandenen Text einsetzen. In diesem Fall wird es für die Bash schwierig zu trennen wo der Variablenname vom Rest getrennt werden muß. Daher gibt es für solche Fälle die Möglichkeit, den Variablennamen mit einer geschweiften Klammer ({}) zu kennzeichnen.

Modifizieren wir also unser Script:

#!/bin/bash

# Sicherungsscript

mount /mnt/usbstick

cd ~

tar czf /mnt/usbstick/${1}_kde.tar.gz .kde
tar czf /mnt/usbstick/${1}_Mail.tar.gz Mail
tar czf /mnt/usbstick/${1}_mozilla.tar.gz .mozilla
tar czf /mnt/usbstick/${1}_Documents.tar.gz Documents

umount /mnt/usbstick

echo "Sicherung wurde durchgeführt"

exit 0

Wenn Sie nun das Script mit sichere heute starten, heissen die Dateien: heute_kde.tar.gz, heute_Mail.tar.gz und so weiter.

Korrekterweise muß ich noch anmerken, daß zu solch einen Script immer auch eine Fehlerbehandlung gehört. Falls z. B. das mounten des Usbsticks fehlschlägt, dann müßte eine Fehlermeldung ausgegeben werden und mit einem Exitstatus ungleich 0 beendet werden. Zur Realisierung solch einer Fehlerbehandlung benötigt man eine Bedingung, wie ich sie nun als nächstes erklären werde.

Bedingungen und Verzweigungen

Um eine Bedingung zu prüfen gibt es die test-Anweisung. Die Syntax für die test-Anweisung ist entweder: test Bedingung oder [ Bedingung ]. Die eckigen Kammern sind also eine alternative Schreibweise für test. Bei Nutzung der eckigen Klammern sind die Leerschritte neben der Klammer unbedingt zu setzen.

Einige mögliche Bedingungen für test sind in folgender Tabelle dargestellt:

Bedingungen für test
AusdruckBeispielErklärung
-d verzeichnis[ -d /tmp ]Ist wahr, wenn die Datei existiert und ein Verzeichnis ist.
-f datei[ -f txt.txt ]Ist wahr, wenn die Datei existiert und eine normale Datei ist.
-w datei[ -w text.txt ]Ist wahr, wenn die Datei existiert und den Schreibzugriff erlaubt.
-x datei[ -x script.sh ]Ist wahr, wenn die Datei existiert und die Ausführung erlaubt.
-n string[ -n "$name" ]Ist wahr, wenn die übergebene Zeichenkette nicht leer ist.
str1 = str2[ "$1" = "Hallo" ]Ist wahr, wenn beide Zeichenketten identisch sind.
z1 -eq z2[ 1 -eq $summe ]Ist wahr, wenn beide Zahlen gleich groß sind (in Bedingungen wird zwischen Zahlen und Zeichenketten unterschieden).
z1 -lt z2[ 17 -lt $zahl ]Ist wahr, wenn die erste Zahl kleiner als die zweite Zahl ist (lt = lower then).
z1 -gt z2[ 28 -gt $tag ]Ist wahr, wenn die erste Zahl größer als die zweite Zahl ist.
z1 -ne z2[ $zahl -ne 7 ]Ist wahr, wenn beide Zahlen ungleich sind.
! ausdruck[ ! 1 -eq $zahl ]Ist wahr, wenn der Ausdruck falsch ist (also eine Negierung).

Darüber hinaus gibt es noch einige weitere Ausdrücke.

Nur mit einer Bedingung, kann ich aber noch nichts anfangen. Ich brauche auch die Möglichkeit auf die Bedingung mit einer Verzweigung zu reagieren. Diese Möglichkeit bietet z. B. der Befehl if.

Befehle die innerhalb eines if-Blocks stehen, werden nur ausgeführt, wenn die vorangegangene Bedingung wahr ist. Außerdem läßt sich optional auch noch ein Anweisungblock definieren, der durchgeführt wird, wenn die Bedingung nicht wahr ist. Die Syntax sieht so aus:

if [ $1 = "Hallo" ] ; then
(Anweisungen die durchgeführt werden, wenn $1 aus der Zeichenkette Hallo besteht)
else
(Anweisungen die durchgeführt werden, wenn $1 nicht aus der Zeichenkette Hallo besteht)
fi

Der Anweisungsblock wird also mit fi geschlossen und mit else kann (muß aber nicht) ein Anweisungsblock eingefügt werden, der bei Nichtzutreffen der Bedingung ausgeführt wird.

Wollte man also beispielsweise in obigen Sicherungsscript eine Fehlerbehandlung einbauen, könnte man direkt nach dem mount-Befehl in etwa folgendes einfügen:

if [ $? -ne 0 ]
then
echo "Mounten schlug fehl!" >&2
exit 1
fi

Dabei steht das $? für den Exitstatus des letzten Befehls. Ist er ungleich 0 (also ein Fehler passiert), wird die Bedingung ausgeführt, in diesem Fall also eine Fehlermeldung ausgegeben und das Script mit einem Exitstatus von 1 beendet. Die Fehlermeldung wird dabei auf die Standardfehlerausgabe geschrieben (>&2).

Daneben kennt die Shell noch viele weitere Möglichkeiten, wie etwa while-Schleifen, case-Anweisungen und for-Schleifen.

Wer nun richtig in die Shellprogrammierung einsteigen will, dem sei das Buch: Shell-Skript Programmierung von Patrick Ditchen empfohlen.

Titelbild des Buches Shell-Skript Programmierung
Zurück nach oben
Shell mit Kommandoprompt