Shellskript unter Linux
Willemers Informatik-Ecke
Wenn Sie mehrere Shell-Befehle immer wieder eingeben müssen, lohnt es sich meist schon, ein Skript anzulegen. Das ist einfach eine Datei, in der die Befehle Zeile für Zeile aufgeschrieben werden. Diese Datei übergeben Sie als Argument an den Befehlsinterpreter bash.
$ bash meinskript
Dadurch wird eine neue Shell bash als Programm gestartet, das das Skript meinskript ausführt. Nachdem alle Befehle der Datei abgearbeitet sind, endet die Shell und kehrt zur aufrufenden Shell zurück.

Skripte ausführen

Sie können die Skriptdatei ausführbar machen und direkt aufrufen. Dazu müssen Sie ihr Ausführungsrechte zuordnen. Dies erreichen Sie über den Befehl chmod.
$ chmod 755 meinskript
Nun kann die Datei direkt aufgerufen werden, ohne dass bash vorangestellt wird.

Um zu vermeiden, dass versehentlich ein boshaft untergeschobenes Skript ausgeführt wird, muss es sich entweder in einem der Verzeichnisse befinden, die durch die Umgebungsvariable PATH definiert wird oder es muss mit dem Dateinamen auch der Pfad genannt werden. Dazu muss nicht der komplette Pfad genannt werden. Falls das Skript im aktuellen Arbeitsverzeichnis liegt, genügt es, den Punkt und ein nachfolgenden Verzeichnistrenner voranzustellen.

$ ./meinskript
Da für die Ausführung in beiden Fällen eine zusätzliche Shell aufgerufen wird, sind Variablen, die in der Skriptdatei gesetzt werden, nach dem Aufruf nicht mehr verfügbar, selbst wenn sie exportiert wurden.

Es gibt aber eine Möglichkeit, eine Skriptdatei aufzurufen, ohne dass eine Extra-Shell gestartet wird. Dazu verwenden Sie einen Punkt anstelle des Shell-Aufrufs.

$ . meinskript
Nun wird das Skript meinskript von der Shell ausgeführt, in der Sie sich gerade befinden. Falls Sie den Punkt zu mager finden, können Sie aber auch den Befehl source verwenden, der das Gleiche tut.

Kommentieren

Das Kommentarzeichen in einer Skriptdatei ist das Doppelkreuz (#). Alles, was in der gleichen Zeile dahinter steht, geht den Interpreter nichts an, hilft Ihnen aber später, wenn Sie sich nicht mehr erinnern können, wozu dieses Skript gut sein sollte und wie es arbeitet.
# Alles fertig, nun noch die Ergebnisse anzeigen:
ls -l   # zeige alle Dateien mit allen Details an

Den Interpreter festlegen

Die erste Zeile können Sie nutzen, um den Interpreter festzulegen. Dies könnte beispielsweise eine andere Shell als bash sein. Sie können aber auch andere Interpreter verwenden, wie beispielsweise Perl oder Python.

Der Interpreter wird im Kommentar in der ersten Zeile festgelegt. Auf das Kommentarzeichen muss direkt ein Ausrufezeichen und dann der genaue Pfad des Interpreters folgen. Für die Bash wäre dies:

#!/bin/bash
Woher wissen Sie, dass bash im Verzeichnis /bin/bash liegt? Dazu rufen Sie den Befehl which auf. Auf die gleiche Weise können Sie herausfinden, wo sich der Interpreter der Sprache Perl befindet.
$ which bash
/bin/bash
$ which perl
/usr/bin/perl
Woher wissen Sie, dass das auf anderen Linux-Rechnern auch dort ist? Weil es in diesen Fällen Standards gibt. Solche Programme liegen in /bin oder /usr/bin.

Zeilen umbrechen

Wenn ein Shell-Kommando länger als eine Zeile wird, können Sie den Befehl aufspalten, indem Sie einen einzelnen Backslash (\) an das Ende der Zeile setzen. Achten Sie darauf, dass kein Leerzeichen hinter dem Backslash steht. Im folgenden Beispiel hat der Befehl cat vier Dateien auf dem Bildschirm ausgegeben:
cat datei1 datei2 \
    datei3 datei4

Abfragen: if

Die Shell stellt wie eine Programmiersprache Abfragen zur Verfügung und verwendet dazu das Schlüsselwort if.

Eine sehr einfache Abfrage sieht so aus:

if true
then
   echo "gut"
else
   echo "schlecht"
fi
Wenn Sie diese Befehlssequenz eingeben, erscheint auf dem Bildschirm das Ergebnis ".gut". Tauschen Sie true gegen false aus, erhalten Sie die Ausgabe "schlecht".
  1. Die Abfrage wird mit dem Schlüselwort if eingeleitet.
  2. Es folgt eine Bedingung.
  3. In einer neuen Zeile wird mit dem Schlüsselwort then ein Block von Befehlen eingeleitet.
  4. Es folgen weitere Zeilen von Befehlen, die bei Erfüllung der Bedingung ausgeführt werden sollen.
  5. Optional folgt in einer weiteren Zeile das Schlüsselwort else.
  6. Dem else folgen weitere Zeilen von Befehlen, die ausgeführt werden sollen, wenn die Bedingung nicht erfüllt ist.
  7. Als Abschluss der Abfrage folgt eine Zeile mit dem Befehl fi.

Bedingungen

Die Bedingungen werden durch den Befehl test ausgewertet. So prüft man, ob eine Datei namens hugo existiert mit dem folgenden Befehl:
if test -f hugo
then
   echo "hugo ist da!"
fi
Wer allerdings andere Programmiersprachen kennt, verwendet statt des Befehls test lieber ein Klammernpaar. Darauf nimmt die Bash Rücksicht. Allerdings werden statt runder Klammern, wie in den meisten Programmiersprachen, bei Bash eckige Klammern verwendet. Und noch eine Besonderheit: Die eckigen Klammern möchten gern von Leerzeichen umgeben sein.
if [ -f *.conf ]
then
   echo "Konfigurationsdatei gefunden"
fi
Es kann geprüft werden, ob eine Datei oder ein Verzeichnis existiert. Dazu können auch Wildcards verwendet werden. Als Bedingungen können auch Zeichenketten verglichen werden. Diese liegen typischerweise in Umgebungsvariablen.
ERG = $(grep Meier *.txt)
if [ $ERG ]
then
   echo "Meier ist dabei"
fi
Es ist auch möglich, Zahlenwerte zu vergleichen. Diese beziehen sich in der Regel auf Umgebungsvariablen. Zur Prüfung auf Gleichheit können neben -eq und -ne auch = und != verwendet werden. Sogar Fans des doppelten Gleichheitszeichens kommen hier auf ihre Kosten.
x=4
y=3
if [ $y -lt $x ]; then echo "ja"; fi
if [ $y != $x ]; then echo "ja"; fi
if [ ! $y = $x ]; then echo "ja"; fi
if [ ! $y == $x ]; then echo "ja"; fi
Die Kleiner- und Größerzeichen können auch verwendet werden, aber mit einem kleinen Kniff. Dann muss die Bedingung in runde Klammern gestellt werden. So wie die eckigen Klammern den Befehl test ersetzen, ersetzen die runden Klammern den Befehl let.
x=4
y=3
if (( $y < $x )); then echo "ja"; fi

Schleife

Um eine Wiederholung zu realisieren, wird statt der if-Anweisung das Schlüsselwort while verwendet.

Zählen

Schon kleine Kinder können zählen, was eine typische Schleife darstellt. Auf den Startwert wird wiederholt 1 aufaddiert bis der Zielwert erreicht wird. Beim Zählen benötigt man eine Variable, in der der aktuelle Zählerstand steht. Diese Variable kann heißen, wie sie will. Ich habe hier den Namen i verwendet.
i=0
while test $i -lt 10
do
    echo $i
    let i=$i+1
done
Der Befehl test ist für Programmierer gewöhnungsbedürftig. Sie mögen lieber Größer- und Kleinerzeichen. Damit diese nicht als Umleitungen interpretiert werden, helfen doppelte runde Klammern.
while  (( $i < 10 ))
Mit dieser Zeile können Sie die while-Zeile im vorigen Listing ersetzen. Beide Zeilen tun exakt dasselbe. Darum ist es Geschmackssache, welche Sie verwenden wollen. Natürlich kann man auch Dateien oder Strings abfragen, also alles verwenden, was die oben genannten booleschen Ausdrücke hergeben.

Die for-Schleife

Die Shell bietet eine besondere Schleife für die Bearbeitung von Dateilisten an. Diese Schleife wird durch den Befehl for eingeleitet. Die Dateiliste wird Datei für Datei in eine Variable gefüttert und diese in je einem Schleifendurchlauf bearbeitet.
for i in *
do
   echo "Gefunden: " $i
done
Die Schleife wird die Variable i in jeder Runde durch das nächste Element der Liste der Dateinamen, die hinter dem \befehl{in} steht, füllen. In diesem Fall steht der Stern für alle Dateien des aktuellen Verzeichnisses. Sie können aber auch eine individuelle Liste mithilfe der Wildcards oder durch einfaches Aufzählen erstellen.

Fallunterscheidung

Insbesondere bei der Auswertung von Parametern wird die Fallunterscheidung case gern eingesetzt. Das folgende Beispiel betrachtet die Variable arg und unterscheidet drei Fälle.
case $arg in
    stop) echo "Halt!" ; return 1 ;;
    -*) echo "Option: $arg" ;;
    *) echo "Argument: $arg" ;;
esac

Auf Parameter zugreifen

Sie können auch ein Shell-Skript mit Parametern aufrufen. Das Skript kann anhand von Pseudovariablen diese Parameter auslesen. Das folgende Beispielskrip tauscht die beiden Argumente und gibt sie aus.
echo $2 $1
Das folgende Skript prüft vor dem Tausch, ob überhaupt zwei Parameter übergeben wurden.
# Skript tauscht seine Parameter
if [ $# = 2 ]
then
    echo $2 $1
else
    echo "Falsche Parameterzahl"
fi

Skriptparameter in einer Schleife auswerten

Wenn Sie alle Parameter auswerten wollen, die der Benutzer übergibt, muss das Skript in der Lage sein, diese nacheinander abzuarbeiten. Im Beispiel werden die übergebenen Werte in einer Schleife einfach nacheinander auf dem Bildschirm wiedergegeben.

In der rechteckigen Klammer wird der erste Parameter als Bedingung verwendet. Das Skript schaut, ob der String nicht leer ist. Dann ist die Bedingung erfüllt und die Schleife wird die Befehle zwischen do und done ausführen.

Der Befehl echo gibt den Parameter aus. Der Befehl shift schiebt die Parameter einen Parameter weiter. Der erste Parameter ist verarbeitet und wird entfernt. Der zweite Parameter wird zum ersten und der dritte Parameter zum zweiten. Solange es also noch Parameter gibt, wird die Schleife wiederholt.

while  [ $1 ]
do
    echo $1
    shift # schiebt die Parameter eine Position weiter
done
Interessant ist es in diesem Zusammenhang, wenn das Skript mit einer Dateimaske aufgerufen wird. Es werden daraufhin alle Dateien angezeigt, die auf die Maske passen. Das verdeutlicht, dass nicht das Programm oder das Skript die Auflösung der Dateimaske übernimmt. Stattdessen wertet die Shell die Maske aus und übergibt dem Programm alle Dateinamen, die darauf passen. Das Skript muss sich also nicht darum kümmern.