Wiki

Shell/Bash-Skripting-Guide für Anfänger

Bash-Skripting-Guide für Anfänger

Dieser Artikel wurde für die folgenden Ubuntu-Versionen getestet:

Dieser Artikel ist größtenteils für alle Ubuntu-Versionen gültig.

Zum Verständnis dieses Artikels sind folgende Seiten hilfreich:

Dieser Artikel soll Interessierten die grundlegenden Möglichkeiten des Bash-Skriptings nahebringen. So werden hier kurz die wichtigsten Konstrukte angesprochen und diese mit einigen praktischen Beispielen etwas vertieft, so dass ein besseres Verständnis ermöglicht wird.

Einsatzgebiete von Bash-Skripten

Bash-Skripte sind sehr flexibel und lassen sich daher für die verschiedensten Aufgaben gut nutzen, zudem erleichtern sie die Arbeit, indem sie es uns erlauben, ständig wiederkehrende Prozesse zu automatisieren. Aufgrund dieser Eigenschaften nehmen sie auch eine zentrale Rolle im Alltag der Systemadministration ein. Hier im Wiki findet man bereits einige Beispiele, welche die breite Anwendbarkeit von Bash-Skripten ein wenig illustrieren.

Vorbereitung

Editor

Zuerst sollte man einen passenden Editor suchen, mit welchem man arbeiten möchte. Standardmäßig sind unter Ubuntu folgende installiert:

Wer mehr auf Konsolenfeeling steht, kann sich mal diese Editoren ansehen:

  • Vim (Nicht leicht für Anfänger, aber trotzdem sehr empfehlenswert)

  • Nano

Eine Übersicht weiterer Editoren findet sich im Artikel Editoren.

Hinweis:

Generell kann es nicht schaden, dass der Editor Syntax-Hervorhebung unterstützt, da dadurch die Übersichtlichkeit stark erhöht wird. Gerade für Anfänger ist das eine große Erleichterung.

Name des Skriptes

Es ist sinnvoll, dass der Name des Skriptes dessen Funktion andeutet. Dabei wäre zu beachten, dass man keine Sonderzeichen verwendet und es sollte nicht schon einen gängigen Systembefehl mit diesem Namen geben. Man sollte sein Skript z.B. nicht cp nennen, da es diesen Befehl schon gibt. Das Ganze lässt sich vermeiden, indem man einfach testet, ob es schon ein Programm mit dem gewünschten Skriptnamen gibt, und das geht folgendermaßen:

which Skriptname 

Erhält man hier eine Pfadangabe zurück, ist der Befehl bereits vorhanden und man sollte sich einen anderen Namen ausdenken. Ist dies nicht der Fall, kann man eine leere Datei mit dem Wunschnamen anlegen.

Ausführbar machen und aufrufen

Damit ein Skript von einer Shell ausgeführt werden kann, muss es zuerst ausführbar gemacht werden. Dies geschieht z.B. in der Konsole durch folgenden Befehl.

chmod +x /pfad/zu/Mein_Skript 

Mehr Informationen dazu findet man auch in Rechte.

Ist das Skript nun ausführbar gemacht, lässt es sich durch den Befehl.:

./Skriptname 

aus dem selben Verzeichnis heraus ausführen [3].

Hinweis:

Man kann das Skript, nachdem es ausführbar gemacht wurde, in einen Ordner (z.B. ~/bin) verlinken oder kopieren, welchen man der Umgebungsvariablen $PATH bekannt macht. Dies ist vor allem dann sinnvoll, wenn ein fertiges Skript von überall her einfach durch die Eingabe des Skriptnamens aufrufbar sein soll.

Es sollte aber (insbesondere bei von mehreren Personen benutzten Rechnern und Servern) darauf geachtet werden, dass Skripte, die in globalen Verzeichnissen (wie /usr/bin/) abgelegt werden, dem Benutzer root gehören und nur dieser in die Datei schreiben darf, ansonsten öffnet man eine Sicherheitslücke.

Zu den Beispielen

Es gibt zwei verschiedene Typen von Beispielen in diesem Artikel. Zum einen kann man interaktive Beispiele finden, welche dann folgendermaßen aussehen.

Befehl... 

Ausgabe...

Dies bedeutet nichts anderes, als dass man dieses Beispiel einfach durch Eintippen in eine Konsole, Zeile für Zeile, nachvollziehen kann. Der zweite Typ ist der eines Skriptes. Diese beginnen immer mit einer Shebang, welche gleich noch erklärt wird, und sind zudem in einer Box mit Syntax-Highlighting gesetzt, damit man die Konstrukte besser erkennen kann. Diese sollten dann in eine Skriptdatei kopiert werden um sie auszuprobieren.

Das erste Skript

Die Shebang

Natürlich ist es möglich, bei jedem Aufruf seines Skriptes die ausführende Shell von Hand festzulegen, doch diesen Aufwand möchte man nicht wirklich treiben. Die übliche Vorgehensweise ist, dies direkt im Skript anzugeben. Dies ermöglicht die Shebang, welche die erste Zeile eines Skriptes darstellt. Hier gibt man nun also an, welche Shell das Skript bearbeiten soll. Dies ist wichtig, da das Skript in der Syntax einer bestimmten Shell verfasst wird, von welcher es dann auch ausgeführt werden sollte.

Die Struktur ist folgende:

1
#!/bin/bash

In diesem Fall legt man also fest, dass für die Ausführung des Skriptes die Bash verwendet werden soll.

Hinweis:

Die Angabe kann hier nach Wunsch variieren und jede andere Shell beinhalten. Hierbei sollte man aber bedenken, dass man sich dann genauer mit der Syntax dieser auseinandersetzen sollte. Dieser Artikel hingegen basiert auf der Syntax der Bash. Es empfiehlt sich jedoch, wo immer möglich /bin/sh zu verwenden – damit wird das Skript (zumindest unter Ubuntu) in der Dash ausgeführt, eine die wesentlich schneller lädt als die Bash, dafür aber auch nicht alle Funktionen der Bash unterstützt.

Kommentare einfügen

Kommentare sind wichtig und man sollte regen Gebrauch davon machen. An schwierigen Passagen sollte man kleine Notizen hinterlegen um festzuhalten, was man sich dabei gedacht hat und wie das Konstrukt funktioniert. So erleichtert man sich selbst, z.B. nach längeren Pausen, oder aber auch Anderen, welche das Skript bearbeiten möchten, das Verständnis. Zudem lassen sich Kommentare auch gut als strukturgebendes Element einsetzen, was eine höhere Übersichtlichkeit ermöglicht.

Einfügen lassen sich Kommentare durch das Hash-Zeichen (#). Dies kann überall in einer Zeile stehen; die Bash ignoriert alles, was dahinter steht. Im Allgemeinen sieht das dann so aus.

# Dies ist ein Kommentar

kein Kommentar # Dies ist ein Kommentar.

### Auf diese Weise kann man beispielsweise Abschnitte trennen ###

...

Variablen

Variablen sind wichtig im Alltag des Bash-Skriptings. Sie verleihen einem Skript große Flexibilität. In ihnen werden z. B. Zeichenketten oder Zahlen abgelegt, welche noch weiterverarbeitet werden sollen.

Variablen belegen

Variablen werden folgendermaßen belegt:

var='hallo' 

In diesem Beispiel wird der Variablen mit dem Namen var der Wert hallo zugewiesen. Hierbei sollte man darauf achten, die Zuweisung in Anführungszeichen zu setzen. Dies ist genau genommen jedoch nur notwendig, wenn die Zuweisung Leerzeichen oder andere Sonderzeichen enthält.

Variablen auslesen

Variablen werden mit echo ausgegeben. Dabei muss ein Dollarzeichen $ vor dem Namen der Variablen eingefügt werden. Für die oben mit der Zuweisung hallo versehene Variable var würde sich also Folgendes ergeben:

echo "$var" 

hallo

Variablen löschen

Man kann Variablen, die nicht mehr gebraucht werden, einfach mit dem Befehl unset wieder löschen. Dies wird am vorherigen Beispiel mit der Variable var weiterverfolgt.

echo "$var" 

hallo

unset var
echo "$var" 

Variablen abgrenzen

Gelegentlich kann es der Fall sein, dass man Variablen innerhalb eines Strings verwenden möchte. Dabei kommt es zu Schwierigkeiten, da die Konsole dann die Variable nicht mehr von dem sie umgebenden String unterscheiden kann. Um dieses Problem zu umgehen, verwendet man eine Schreibweise mit geschweiften Klammern. Ein Beispiel zeigt das Problem.

echo "$var" 
hallo
echo "$varlolo" 

Hier wird nach dem Anhängen eines beliebigen Strings an die Variable $var eine leere Variable ausgegeben. Dies liegt daran, dass die Bash versucht, die Variable mit dem Namen $varlolo auszugeben, welche nicht belegt und daher eben leer ist. Grenzt man die Variable jedoch durch die Schreibweise mit geschweiften Klammern ab, bekommt man folgendes Ergebnis.

echo "${var}lolo" 

hallololo

Wie man sehen kann, wird hier der angehängte String "lolo" an die Variable angehängt, ohne sich mit dem String der Variablen zu vermischen und man erhält das gewünschte Ergebnis.

Hinweis:

Oft wird diese Schreibweise gegenüber der ohne Klammern bevorzugt. Ich werde dies in diesem Artikel ab hier auch so handhaben.

Ausgaben in eine Variable schreiben

Befehlssubstitution heißt hier das Zauberwort. Damit ist es möglich, die Ausgaben eines beliebigen Befehls in eine Variable zu schreiben. Ein Beispiel soll hier zeigen, wie man z.B. die Namen der Dateien im Homeverzeichnis in eine Variable einliest.

1
2
3
4
5
6
7
8
9
#!/bin/bash
#Ausgaben in Variable schreiben

#Variablendefinition
VERZEICHNIS="/home/foo/"

NAMEN="$(ls -A ${VERZEICHNIS})"

echo ${NAMEN}

Wie man sieht, wird alles zwischen $( und ) stehende als Befehl ausgeführt und das Ergebnis zurückgeliefert. Somit gelangen die erhaltenen Daten dann durch die Zuweisung in die Variable $NAMEN. Danach werden die so gewonnenen Daten im Falle dieses Beispiels dann einfach allesamt auf einmal ausgegeben durch echo. Sie lassen sich so aber auch sehr gut über eine Schleife abarbeiten, dazu aber später mehr.

Hinweis:

Es gibt eine alternative Schreibweise zu diesem Operator, die so genannten Backticks `: NAME="whoami". Der Einsatz empfiehlt sich aber nur bei kurzen Befehlen, da sonst der Quellcode leicht unleserlich wird.

Abschneiden von Mustern

Eine gewöhnungsbedürftige, aber doch sehr nette Funktion ist das Herausschneiden bestimmter Muster aus dem String einer Variablen.

Schnitt-Funktionen
Funktion Erklärung
${variable%muster} Entfernt rechts das kleinste passende Stück.
${variable%%muster} Entfernt rechts das größte passende Stück.
${variable#muster} Entfernt links das kleinste passende Stück.
${variable##muster} Entfernt links das größte passende Stück

An einem kleinen Beispiel möchte ich hier kurz eine Anwendungsmöglichkeit erläutern.

pfadname="/var/www/index.html"

echo ${pfadname} 
/var/www/index.html
echo "Pfad: ${pfadname%/*}" 
Pfad: /var/www
echo "Dateiname: ${pfadname##*/}" 
Dateiname: index.html

Hier sieht man, wie leicht man an Teile einer Variablen herankommt, ohne die ursprüngliche Variable zu verändern. Ergänzend sei hier noch gesagt, dass ein Metazeichen (*) zum Einsatz gekommen ist, welches für eine beliebige Zeichenkette an dieser Stelle steht. Dies ist natürlich nicht die einzige Möglichkeit. Hier kann jedes Zeichen oder beliebige Folge von Metazeichen zum Einsatz kommen.

Umgebungsvariablen

Es gibt diverse von der Shell selbst verwaltete Variablen. Diese Umgebungsvariablen lassen sich gut für verschiedene Aufgaben in unseren Skripten verwenden, wie z.B. einer Fehlerkontrolle von Programmen, indem man den Exitstatus prüft. In der folgenden Liste werden nur ein paar dieser Variablen aufgeführt, für weitere Informationen ist bitte der Artikel Umgebungsvariablen zu bemühen.

Umleitungen
Variable Erklärung
HOME Enthält das Homverzeichnis des Benutzers.
PATH Suchpfad für Kommandos.
PWD Enthält das Aktuelle Verzeichnis.
? Enthält den Exitstatus des letzten Kommandos

Arrays

Arrays werden auch als Feldvariablen bezeichnet. Sie ermöglichen es, mehrere Werte gleicher Art innerhalb einer Variable abzuspeichern. Die einzelnen Werte stehen dabei in sogenannten Feldern des Arrays und können über den Index des Feldes einzeln angesprochen werden. Arrays lassen sich hervorragend über Schleifen verarbeiten, was etwas weiter unten noch beschrieben wird.

Arrays belegen

Um einen Wert in ein Array zu schreiben, ist es nötig den Index mit anzugeben. Allgemein sieht das folgendermaßen aus.

Arrayname[n]="Wert" 

Ganz vorne steht der Name des Arrays, gefolgt vom Index, welcher durch eine Zahl in eckigen Klammern angegeben wird. Dann folgt wie auch schon bei den Variablen eine Zuweisung. In diesem Beispiel wird also der zugewiesene Wert "Wert" in das Feld "n" des Arrays "Arrayname" geschrieben.

Arrays auslesen

Arrays lassen sich genau wie die Variablen mit Hilfe des Befehls echo auslesen. Hierbei sind jedoch verschiedene Möglichkeiten gegeben. Dieser Befehl ermöglicht den Zugriff auf das komplette Array.

echo ${array[*]}  

Durch den Stern in den Klammern für den Index erreicht man die Ausgabe des ganzen Arrays. Zudem gibt es auch die Möglichkeit auf einzelne Elemente zuzugreifen.

echo ${array[2]} 

Dabei wird einfach über die Angabe des Index das entsprechende Element ausgegeben.

Arrays löschen

Arrays lassen sich genau wie Variablen mit dem Befehl unset löschen.

unset array 

Beispiel für ein Array

Das folgende Beispiel zeigt etwas ausführlicher den Umgang mit einem Array.

array[1]="Hallo"

echo ${array[*]} 
Hallo
array[2]="Pingu"

echo ${array[*]} 
Hallo Pingu
array[1]="Bye"

echo ${array[*]} 
Bye Pingu
unset array

echo ${array[*]} 

An diesem Beispiel kann man ganz gut verschiedene Aspekte des Umgangs mit einem Array erkennen. Es zeigt wie ein Array angelegt wird, wie neue Felder belegt oder diese einfach verändert werden können. Schlussendlich wird das Array dann mit unset gelöscht.

Wiki/Icons/users.png

Interaktion mit dem Benutzer

Oft ist es notwendig dem Benutzer des Skriptes während dem Durchlauf mitzuteilen was gerade passiert oder ihn aufzufordern gewisse Angaben zu machen die für die weitere Arbeit des Skriptes notwendig sind. Den Möglichkeiten dies umzusetzen wird sich der folgende Abschnitt widmen.

Ausgabe von Text

Echo kann man verwenden um den Benutzer des Skriptes wissen zu lassen was gerade passiert, oder um ihn aufzufordern eine Eingabe zu machen. Hierbei sollte man darauf achten, den auszugebenden Text in Anführungszeichen zu setzen.

1
2
3
4
5
6
#!/bin/bash
#Hallo Welt Skript

echo "Hallo Welt!"

exit 0

Die Shebang wurde bereits beschrieben, ebenso Kommentare. Mit "echo" gibt man Nachrichten auf der Standardausgabe aus, welche somit also am Bildschirm sichtbar werden. Der exit Status gibt an, dass das Skript einwandfrei beendet wurde (also mit 0), aber exit 0 am Scriptende kann weggelassen werden. Mitten im Script kann man es mit exit vorzeitig beenden. Dieses Beispiel würde folgenden Output generieren.

Hallo Welt!

Einlesen von Eingaben

Mit Read ist man in der Lage Eingaben von der Standardeingabe einzulesen, und somit kann man also Benutzereingaben verarbeiten.

1
2
3
4
5
6
7
#!/bin/bash
#Begrüßung

echo -n "Geben sie ihren Namen ein: "
read NAME

echo "Hallo: ${NAME}"

In diesem Beispiel fordert man den Benutzer des Skriptes dazu auf eine Eingabe zu machen. Dabei dient der Schalter "-n" hinter echo dazu, die Ausgabe einer neuen Zeile zu verhindern. Dies bedeutet im Klartext, man schreibt dann den Namen direkt hinter den Doppelpunkt der ausgegebenen Aufforderung. Mit read liest man die Eingabe in die hinter read stehende Variable "NAME" ein. Die Ausgabe des Skriptes sieht dann wie folgt aus:

./begruessung.sh 
Geben sie ihren Namen ein: Sab

Hallo: Sab.

Umleitungen

Umleitungen werden zu verschiedenen Zwecken verwendet. Hier werden nun ein Paar dieser Möglichkeiten aufgelistet. Mehr zum Thema Umleitungen findet sich im Wiki unter dem Artikel Umleitungen.

Umleitungen
Operator Erklärung
> Dient der Umleitung der Standardausgabe in eine Datei.
>> Dient ebenfalls der Umleitung in eine Datei, hängt jedoch die Ausgabe an eine bereits bestehende Datei an.
| Mit dem Operator für eine Pipe lässt sich die Ausgabe eines Tools als Eingabe für ein anderes Tool verwenden.

Das Test-Kommando

Das Test-Kommando wird dazu benötigt auf diverse Bedingungen hin zu testen. Es wird oft in Zusammenhang mit Programmführenden Konstrukten verwendet. Test kennt verschiedene Operatoren welche die Bedingungen des Testes beeinflussen. In der Folgenden Tabelle sind die Möglichkeiten von Test gelistet.

Die Optionen des Test-Kommandos
Operator Erklärung
-d Verzeichnis Ist wahr wenn "Verzeichnis" existiert.
-f Datei Ist wahr wenn Datei existiert.
-w Datei Ist wahr wenn die Datei existiert und Schreibzugriff erlaubt ist.
-x Datei Ist wahr wenn Datei existiert und Ausführbar ist.
-n String Ist wahr wenn String nicht leer ist. (Bsp. eine Variable)
String1 == String2 Ist wahr wenn String1 gleich String2 ist.
Zahl1 -eq Zahl2 Ist wahr wenn Zahl1 gleich Zahl2 ist. (-eq = equal)
Zahl1 -lt Zahl2 Ist wahr wenn Zahl1 kleiner Zahl2 ist. (-lt = less than)
Zahl1 -gt Zahl2 Ist wahr wenn Zahl1 größer Zahl2 ist. (-gt = greater than)
Zahl1 -le Zahl2 Ist wahr wenn Zahl1 kleiner oder gleich Zahl2 ist. (-le = less or equal)
Zahl1 -ge Zahl2 Ist wahr wenn Zahl1 größer oder gleich Zahl2 ist. (-ge = greater or equal)
Zahl1 -ne Zahl2 Ist wahr wenn Zahl1 nicht gleich Zahl2 ist. (-ne = not equal)
! String Ist wahr wenn String falsch ist, also eine Negation.

Wie man das Testkommando nun richtig anwendet, wird gleich im Abschnitt Programmführung noch gezeigt.

Programmführung

Wie die Überschrift schon vermuten lässt geht es hier darum die Programmführung zu organisieren. Dabei nimmt man mit verschiedenen Konstrukten Einfluss auf den Ablauf des Skriptes.

If-Else-Anweisung

Diese Anweisung macht es möglich nach einem Test auf eine Bedingung hin, eine entsprechende Maßnahme zu ergreifen. Das Konstrukt ist folgendermaßen aufgebaut.

1
2
3
4
if [ Test-Bedingung ]
  then
    Befehl...
fi

In den Eckigen Klammern kann man jetzt jede der zuvor kennengelernten Option des Test-Befehls benutzen. Der Anweisungsblock wird mit fi geschlossen. Man kann dieses Konstrukt auch noch etwas erweitern, um eine alternative Reaktion auf einen Test hinzuzufügen.

1
2
3
4
5
6
if [ Test-Bedingung ]
  then
    echo "Ok, alles in Ordnung."
  else
    echo "Ahrg...Fehler."
fi

Dies wird durch den Zusatz "else" ermöglicht. Zudem lässt sich dieses Konstrukt noch beliebig durch weitere Test-Bedingungen mit dem Zusatz "elif" erweitern. Dies sieht im Allgemeinen folgendermaßen aus.

1
2
3
4
5
6
7
8
9
if [ Test-Bedingung ]
  then
    Befehl...
  elif [ Test-Bedingung ]
    then
      Befehl...
  else
    Befehl...
fi

Ein Beispiel zeigt wie man auf die Existenz einer Datei hin prüft und entsprechend des Ergebnisses eine Meldung dazu ausgibt.

1
2
3
4
5
6
7
8
9
#!/bin/bash
#Dateitest

if [ -f /home/Pingu/test.txt ]
  then
    echo "Die Datei test.txt in Pingus Home ist da."
  else
    echo "Die Datei test.txt in Pingus Home fehlt."
fi

Oder das ganze mit einer Elif Konstruktion erweitert.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash
#Dateitest

if [ -f /home/Pingu/test.txt ]
  then
    echo "Die Datei test.txt in Pingus Home ist da."
  elif [ -d /home/Pingu/test.txt ]
    then
      echo "test.txt ist ein Verzeichnis"
  else
    echo "Die Datei test.txt in Pingus Home fehlt"
fi

Case Anweisungen

Die Case Anweisung ist ein wichtiges Konstrukt um nach dem Test einer Variable entsprechend auf deren Inhalt zu reagieren. Sie ist übrigens auch eine elegante Alternative zu verschachtelten If-Else-Konstrukten, mit welchen man im Prinzip das gleiche Ergebnis erzielen könnte. Im Allgemeinen sieht dies folgendermaßen aus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
case "${VARIABLE}" in
        wert1) Befehl...
            ;;
        wert2) Befehl...
               Befehl...
               Befehl...
            ;;
        wert3) Befehl...
               Befehl...
            ;;
        wert4) Befehl...
            ;;
            *) Befehl...
               Befehl...
            ;;
esac

Hier wird die Variable "VARIABLE" auf verschiedene Möglichkeiten hin überprüft welche in "wert1" bis "wert4" definiert werden. Zudem besteht die Möglichkeit, sollte keine der gegebenen Muster auf die Variable zutreffen eine Alternative Behandlung auszuführen. Dies ist durch den Abschnitt "*)" gegeben. Jeder Abschitt wird durch ein zweimaliges Semikolon ";" beendet und darf nicht vergessen werden.

Ein Beispiel zeigt dies nochmal etwas deutlicher.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/bin/bash
#namenstest

echo -n "Bitte geben sie ihren Namen ein: "
read NAME

case "${NAME}" in
        Frank) echo "Hallo Frank"
            ;;
        Werner) echo "Hallo Werner"
            ;;
        Fritz) echo "Hallo Fritz" 
            ;;
            *) echo "Hallo Fremder" 
            ;;
esac

Es lassen sich auch Alternativen für eine Testbedingung angeben. Dazu wird der Pipe-Operator "|" verwendet. Dies könnte dann ungefähr folgendermaßen aussehen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/bash
#installer

echo -n "Installer: Adminrechte werden benötigt. Wollen sie fortfahren?: "
read ANSWER

case "${ANSWER}" in
        Yes|yes|Y|y|"") echo "Ok los geht's"
            ;;
        No|no|N|n) echo "Abbruch."
                   exit 1
            ;;
            *) echo "Unbekannter Parameter" 
            ;;
esac

So wird jetzt der erste Eintrag ausgeführt für Yes,yes,Y,y und zudem ist noch ein Leerzeichen angegeben welches durch die Anführungsstriche symbolisiert wird. Dies dient dazu, diesen Eintrag im Prinzip als Standard zu definieren, so wird dieser auch schon ausgeführt wenn man nur die Eingabetaste betätigt.

Schleifen

Es gibt in der Bash drei verschiedene Arten von Schleifen welche unterschiedlich gut für einen jeweiligen Typ von Aufgabe verwendet werden können. Grundsätzlich wird es durch sie möglich einen Befehl mehrmals auszuführen bis eine Bedingung eintritt oder solange eine Bedingung erfüllt ist. Diese drei Typen sind unten aufgezeigt, jedoch möchte ich mich auf die For- und While-Schleife beschränken.

  • For-Schleife

  • While-Schleife

  • Until-Schleife

Die wohl am häufigsten verwendete Schleife ist die For-Schleife. Die Syntax dafür sieht folgendermaßen aus.

1
2
3
4
5
6
for variable in "Parameterliste"
  do
    Befehl1
    Befehl2
    usw...
done

Diese Schleife beendet sich, sobald das letzte Element der Übergebenen Parameterliste abgearbeitet ist. Ein Beispiel soll ihre Arbeitsweise verdeutlichen.

1
2
3
4
5
6
#!/bin/bash
# for-schleife
for wort in "Hallo" "du" "Welt" "da" "draussen"
  do
    echo ${wort}
done

Dieses Beispiel erzeugt den folgenden Output.

./forschleife 
Hallo
du
Welt
da
draussen

Jetzt möchte ich noch einmal auf das Beispiel von vorher zurück kommen bei dem man eine Variable mit Hilfe der Ausgabe eines Befehles gefüllt hat. Dies kann man sehr gut mit einer For-Schleife verbinden und so z.B. folgendes realisieren.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash

#Variablendefinition

INPUTDIR="/home/foo/Bilder/ZuBearbeiten/"

OUTPUTDIR="/home/foo/Bilder/Bearbeitet/"

PICS="`ls -A "${INPUTDIR}" | grep -i jpg`"

#Abarbeiten der eingelesenen Bilder mit Hilfe einer For-Schleife
#und dem Tool Imagemagick.

for PIC in ${PICS}
  do
    echo "Bearbeite Bild:    ${PIC}"
    echo "-------------------------------------------0"
    convert ${INPUTDIR}${PIC} -colorspace Gray ${OUTPUTDIR}${PIC}
done

Dieses Skript schreibt zuerst einmal alle Namen unserer Bilddateien im Ordner "INPUTDIR" in die Variable "PICS". Dazu wird hier die Ausgabe von ls durch eine Pipe (Dies ist eine Umleitung) an Grep weitergeleitet um die Bilddateien Herauszufischen. Dabei dient der Schalter "-i" bei "grep" dazu groß und Kleinschreibung zu ignorieren. Die Bilddateien werden dann über die For-Schleife abgearbeitet, wobei das Programm Imagemagick zum einsatz kommt. Hierbei werden alle Farbbilder in ihre Grauwerte umgerechnet und dann im Ordner "OUTPUTDIR" gespeichert. Man kann hieran ganz gut erkennen wie eine For-Schleife arbeitet. Jedes Element in der Liste wird einzeln herangezogen und abgearbeitet. Dazu bedient man sich einer weiteren Variable, welche diese jeweils aufnimmt (hier PIC). Die Schleife endet nach der Bearbeitung des letzten Listeneintrages.

Diese Methode lässt sich auch auf Arrays übertragen. Hier sieht jedoch das Konstrukt etwas anders aus. Hier einmal allgemein.

1
2
3
4
for WERT in ${ARRAY[*]}
  do
    Befehl...
done

Die While-Schleife sieht im Allgemeinen folgendermaßen aus.

1
2
3
4
5
6
while [ Test-Bedingung ]
do
    Befehl1
    Befehl2
    usw...
done

Die While-Schleife wird solange ausgeführt wie eine Bedingung zutrifft oder eben nicht zutrifft. Ein Beispiel zeigt wie man mit einer While-Schleife wartet bis eine bestimmte Benutzereingabe stattgefunden hat. Hier kombiniert man einfach eine Case-Anweisung mit der While-Schleife.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash
#Warten auf das richtige Ergebnis.

echo "Frage: Was macht 2 + 2"
echo "-------------------------o"
echo ""
while true
  do
    echo -n "Bitte ihre Eingabe:"
    read ANSWER
    case "${ANSWER}" in
      4) echo "Richtig"
          break
          ;;
      *) echo "Falsch" 
          ;;
    esac
done

Diese While-Schleife wurde mit "while true" initialisiert. Sie würde also im Prinzip nie enden. Einen Ausbruch hieraus ermöglicht uns der Befehl "break". Ist die Antwort richtig wird in diesem Fall also die Schleife beendet, andernfalls läuft sie einfach immer weiter. Es wäre noch zu erwähnen, dass diese Schleife natürlich auch mit jeder anderen Test-Bedingung initialisiert werden kann.

Parameter an ein Skript übergeben

Man kann diverse Eingaben beim Start eines Skriptes mit an das Skript übergeben. Diese befinden sich dann in den Variablen $1 bis $9. So kann man dann also leicht innerhalb des Skriptes auf die übergebenen Parameter zugreifen. Dies kann auch geschickt dazu genutzt werden, das Skript in seiner Arbeit zu beeinflussen. Ein Beispiel soll hier zeigen wie dies generell funktioniert.

1
2
3
4
5
6
7
8
#!/bin/bash
# willkommen
if [ ${1} == "Pingu" ]
  then
    echo "Hallo kleiner Pingu."
  else
    echo "Hallo ${1}"
fi

Und jetzt der Aufruf des Skriptes.

./willkommen.sh Pingu 
Hallo kleiner Pingu.
./willkommen.sh Peter 
Hallo Peter

Das Ganze lässt sich durch den Befehl shift noch weiter Ausbauen und man kann somit auch mehr als nur 9 Parameter übergeben. Mit Shift verschiebt man dabei die Angaben welche auf der Kommandozeile übergeben werden nach Abarbeitung immer eins weiter nach links. Hier bedient mach sich sogenannter short-circuit-Tests, die im Prinzip wie If arbeiten. Wie das funktioniert, soll hier nur ganz kurz gezeigt werden.

1
2
3
4
5
6
7
8
#!/bin/bash

while [ ${1} != '' ]
  do
    [ ${1} == "-b" ] && BACKUP="yes" && echo "Ok. Mache ein Backup" && shift
    [ ${1} == "-r" ] && RESTORE="yes" && echo "Ok. Mache ein Restore" && shift
    [ ${1} == "-c" ] && CLEAN="yes" && echo "Ok. Räume auf" && shift
done

Die Bedingung, mit der die While-Schleife initiiert wird, bedeutet soviel wie: Mach weiter solange "$1" nicht leer ist. Gibt man nun mehrere Parameter an das Skript mit, so wird immer der in Position "$1" liegende bearbeitet. Ist dies geschehen, wird durch "shift" der Parameter, welcher sich an der Stelle "$2" befindet, in die 1. Position gerückt, und die Schleife beginnt von vorne. Zudem wäre noch zu sagen, dass durch die Und-Zeichen "&&" Befehle in Short-Circuit-Tests verknüpft werden.

Desweiteren wäre in diesem Zusammenhang noch die Option extglob interessant, mit der sich recht komplexe testmuster bei der Übergabe von Parametern realisieren lassen. Wer aber nicht viel herumbasteln will, was die Übergabe von Parametern angeht, kann auf den Befehl "getopts" zurückgreifen, welcher schnell und komfortabel die Abarbeitung der Parameter übernimmt. Ein Beispiel soll zeigen wie "getopts" arbeitet.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash
# Getopttest

while getopts ':brcp:' OPTION ; do
  case ${OPTION} in
    b)   echo "Ok mache ein Backup";;
    p)   echo "Backuppfad ist: $OPTARG";;
    r)   echo "OK mache ein Restore";;
    c)   echo "Ok mache  sauber danach";;
    *)   echo "Unbekannter Parameter"
  esac
done

Wie man sehen kann arbeitet "getopts" mit bereits vertrauten Konstrukten zusammen. Es kommen eine While-Schleife und eine Case Anweisung zum Einsatz. Zur Notation von "getopts" wären folgende Dinge zu sagen. Der erste Doppelpunkt in der Parameterliste schaltet die Anzeige von Fehlermeldungen ab. Dann folgen normale Parameter. Besitzt ein Parameter einen Doppelpunkt hinter sich, bedeutet dies, dass er ein Argument benötigt. Hier in diesem Beispiel ist das bei "p" der Fall. Die Option "p" nimmt als Argument einen Pfad auf. Dieser wird dann in der Variable "${OPTARG}" gepeichert worüber man dann auch Zugriff darauf hat.

Funktionen

Funktionen helfen die Übersichtlichkeit zu verbessern, sie bieten die Möglichkeit mehrere Befehle zu logischen Gruppen zusammenfassen. Wurden sie einmal definiert sind sie im Skript durch Ihren Namen abrufbar. Dabei ist zu beachten, dass die Definition vor dem Aufruf stattgefunden haben muss. Dies führt dazu, dass man erst die Funktionen definiert und diese dann in einem Hauptteil, unten im Skript aufruft. Durch diese Vorgehensweise bleibt auch der logische Fluss des Skriptes übersichtlicher erhalten. Im Allgemeinen sieht eine Funktion dann folgendermaßen aus.

1
2
3
funktionsname(){
Befehle...
}

Ein Beispiel zeigt wie dies aussehen könnte.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash
# Variablendefinition

PFAD="/home/pingu/"

# Funktionsdefinitionen
wievieledateien(){
    ANZAHL="`ls -lA ${PFAD} | wc -l`"
    echo "Es befinden sich ${ANZAHL} Dateien und Ordner in ${PFAD}"
}
# Hauptteil
wievieledateien

Starten man nun dieses Skript erhält man folgende Ausgabe.

./wievieledateien 
Es befinden sich 90 Dateien und Ordner in /home/pingu/

Zudem ist es möglich Parameter an eine Funktion zu übergeben, was diese noch viel mächtiger macht. Dies funktioniert genau wie bei der Übergabe von Parametern an ein Skript, indem man beim Aufruf die Parameter an den Befehl anhängt. Das folgende Beispiel baut das vorherige dahingehend aus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/bash
# Funktionsdefinitionen
wievieledateien(){
# Substitution der Parametervariablen
    ZAEHLPFAD="${1}"
    ANZAHL="`ls -lA ${ZAEHLPFAD} | wc -l`"
    echo "Es befinden sich ${ANZAHL} Dateien und Ordner in ${ZAEHLPFAD}"
}
# Hauptteil
wievieledateien /home/pingu/

Grafische Menüs

Es ist möglich ein Skript durch zusätzliche Programme grafisch aufzuwerten. Dafür gibt es verschiedene Möglichkeiten. Wie das ganze funktioniert soll der folgende Abschnitt klären. Hauptsächlich soll dabei auf das Programm "Dialog" eingegangen werden.

Dialog bietet eine, immer noch konsolenbasierte, Aufbereitung der darzustellenden Daten. Dabei reicht das Repertoire von einfachen Entscheidungsfenstern (Ja/Nein) über verschiedene Auswahlmenüs bis hin zur Möglichkeit der Darstellung einer Dateiauswahlliste. Da Dialog standardmäßig nicht installiert ist sollte man dies erst einmal tun. Der folgende Befehl installiert Dialog.

sudo apt-get install dialog 

Ein einfaches Beispiel soll die Arbeit mit Dialog illustrieren.

1
2
3
4
#!/bin/bash
# Ja oder Nein

dialog --yesno "Bist du ein Linuxfan?" 15 60

Dieses Beispiel erzeugt eine Box von 60 Zeichen Breite und 15 Zeilen Höhe mit dem in Anführungsstrichen stehenden Fragentext und zwei Buttons für "Yes" und "No". Dialog speichert den Rückgabewert für die getroffene Auswahl in der allgemeinen Rückgabevariable "$?". Im Fall dieser Yes/No-Box bedeuten die Rückabewerte 0 ja, 1 nein und 255 Abbruch ohne Auswahl. Jetzt möchte ich einen Schritt weiter gehen und dieses Beispiel ausbauen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
# erweiterter Dialog

dialog --backtitle Umfrage --title "Erste Frage" --yesno "Sind sie ein Linuxfan?" 15 60
ANTWORT=${?}

if [ ${ANTWORT} -eq "255" ]
  then
    echo "Abgebrochen"
    exit 255
fi

if [ ${ANTWORT} -eq "1" ]
  then
    dialog --backtitle Umfrage --title Kritikbox --msgbox "Na dann wird es aber allerhöchste Zeit!" 15 40
fi

if [ ${ANTWORT} -eq "0" ]
  then
    dialog --backtitle Umfrage --title Schmeichelbox --msgbox "Hm. Das hab ich mir fast schon gedacht!" 15 40
fi

Hier tauchen neue Optionen auf. Die Eine ist "--backtitle". Wie der Name schon sagt, ist diese dafür zuständig den Titel des Hintergrundes zu setzen. Die Andere ist "--title" welche für die Überschrift der eigentlichen Box zuständig ist. Zusätzlich zur "--yesno"-Box hat man es hier noch mit zwei Messageboxen (--msgbox) zu tun, welche nur eine Nachricht darstellen und dann über einen OK-Button geschlossen werden können. Diese Messageboxen tauchen je nach der im ersten Fenster gegebenen Antwort auf. Jetz noch etwas komplexer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/bin/bash
# erweiterter Dialog

dialog --backtitle Umfrage --title "Erste Frage" --yesno "Sind sie ein Linuxfan?" 15 60
ANTWORT=${?}

if [ ${ANTWORT} -eq "255" ]
  then
    echo "Abgebrochen"
    exit 255
fi

if [ ${ANTWORT} -eq "1" ]
  then
    dialog --backtitle Umfrage --title Kritikbox --msgbox "Na dann wird es aber allerhöchste Zeit!" 15 40
fi

if [ ${ANTWORT} -eq "0" ]
  then
    dialog --backtitle Umfrage --title Lobbox --msgbox "Hm. Das hab ich mir fast schon gedacht!" 15 40

AUSWAHL=`dialog --stdout --backtitle Umfrage --title Details --radiolist "Welche Desktopumgebung bevorzugen Sie? Sie können nur eine wählen." 16 60 5 \
     "Gnome" "Die Gnome Desktop Umgebung" off \
     "KDE" "Die KDE Desktopumgebung" on \
     "Sonstige" "Eine andere hier nicht gelistete" off`
fi

case ${AUSWAHL} in
  Gnome)
    dialog --backtitle Umfrage --title Reaktion --msgbox "Sehr schön Gnome!" 15 40
    ;;
  KDE)
    dialog --backtitle Umfrage --title Reaktion --msgbox "Sehr schön KDE!" 15 40
    ;;
  Sonstige)
    dialog --backtitle Umfrage --title Reaktion --msgbox "Hm Sonstige!" 15 40
    ;;
esac

Dieses Beispiel geht nach einer positiv ausgefallenen Antwort noch etwas weiter. Mit Hilfe einer Radioliste "--radiolist" wird die bevorzugte Desktopumgebung erfragt. Eine Radioliste zeichnet sich dadurch aus, dass man nur einen der Einträge aktivieren kann. Möchte man mehrere Einträge aktivieren können sollte man statdessen "--checkbox" verwenden. Die Antwort aus unserer Radioliste wird mit hilfe von Backticks in eine Variable geschrieben um sie danach weiterverarbeiten zu können. Dies geschieht durch eine Case Anweisung. Was noch wichtig wäre zu sagen ist, dass die Option "--stdout" beim schreiben in eine Variable nicht vergessen werden sollte. Diese sorgt für eine Ausgabe auf dem Bildschirm. Wäre sie nicht gesetzt würde der Dialog nicht erscheinen.

Als weitere Möglichkeiten zur Erstellung grafischer Unterstützungen für Bash-Skritpe wären noch die Programme Zenity und KDialog zu nennen. Wie dieses Programme funktionieren wird genauer in deren jeweiligen Artikeln hier im Wiki beschrieben.

Diese Revision wurde am 3. März 2010 um 13:42 Uhr von dAnjou erstellt.
Dieser Seite wurden folgende Begriffe zugeordnet: Shell, Programmierung, Einsteiger

Passwort vergessen?