Die UNIX-Shell ist der Kommandointerpreter auf einem Unix- und Linux-System, die Möglichkeiten gehen aber weit darüber hinaus, was Sie vielleicht vom Kommandointerpreter eines MS-DOS Systems (COMMAND.COM) her kennen. Er liest die vom Benutzer eingegebenen Kommandos und bringt diese nach einigen Aufbereitungen zur Ausführung. Desweiteren kennen Shells Konstrukte, die von höheren Programmiersprachen bekannt sind wie Schleifen, Verzweigungen und Funktionen. Mit diesen Konstrukten ist es möglich, Aufgaben effizient zu lösen, ohne direkt in einer Programmiersprache wie Perl oder C zu programmieren. Das macht die Shell zu einem mächtigen und unverzichtbarem Werkzeug für System-Administratoren, Programmierer und allen, die routinemäßige Abläufe automatisch steuern möchten.
Die Shell hat ihren Namen daher, weil sie sich wie eine Muschel (engl. Shell) um den Betriebssystemkern legt. Direkte Kommunikation mit dem Betriebssystemkern wäre auch viel zu komplex und benutzerunfreundlich. Dabei nimmt die Shell die Kommandos vom Benutzer entgegen, interpretiert diese und setzt sie in Systemaufrufe um. Die Rückmeldungen des Systems werden wieder von der Shell entgegengenommen, aufbereitet und an den Benutzer ausgegeben. UNIX-Shells sind kein Bestandteil des Betriebssystems, sondern eigenständige Programme. Diese Tatsache hat einige Vorteile - Shells können wie andere Anwendungsprogramme aufgerufen und auch ausgetauscht werden. Daraus folgt, daß es nicht die UNIX-Shell gibt, sondern viele verschiedene mit unterschiedlicher Leistungsfähigkeit und Syntax. Von einigen UNIX-Linien wurden eigene Shell-Varianten entwickelt.
Nachfolgend werden die einzelnen Shells kurz beschrieben, wobei diese Liste nicht vollständig ist.
Die Bourne-Shell ist die erste UNIX-Shell überhaupt. Ihr Erfinder ist Steve Bourne, daher auch der Name und bildet die Grundmenge aller Shells.
Eine Weiterentwicklung der Bourne-Shell, Erfinder ist David Korn. Sie bietet Verbesserungen gegenüber der Bourne-Shell und eine History-Funktion.
Die BASH ist der Nachfolger der Bourne-Shell und die Standardshell unter Linux-Systemen. Eine History-Funktion ist implementiert. Derzeit gibt es zwei Versionen der BASH (Version 1.x und 2.x), wobei die Version 2.x sehr viel restriktiver in der Syntax ist.
Sie wurde auf der BSD-UNIX1) Linie entwickelt und basiert auf der Bourne-Shell. Ihre Syntax ist stark an der Programmiersprache C angelehnt (daher auch der Name) und ist deswegen bei Programmierern sehr beliebt. Zusätzlich zu den Funktionen der Bourne-Shell bietet sie Job-Kontrolle und eine History-Funktion.
Die TC-Shell ist der Nachfolger von der C-Shell mit Erweiterungen und Neuigkeiten.
Sie ist ein Korn-Shell Clone.
Eine Bourne-kompatible Shell aus der NetBSD-Linie. Da sie nur 62 kByte groß ist, eignet sie sich gerade für Rechner mit wenig Arbeitspeicher sowie Installation- und Rettungs-Disks, dafür bietet sie keine Job-Kontrolle2).
Diese Shell ist ein Zusammenschluß von mehreren Shells, sie besitzt die meisten Funktionen der ksh, Features der csh und einige Optionen der tcsh.
Die Bourne-Again-Shell, kurz bash, ist die Standard-Shell auf Linux-System und wird deswegen in diesem Kapitel näher besprochen. Viele dieser Funktionen sind auch übertragbar auf andere Shells, manchmal mit leicht abgeänderter Syntax. Bei der Einrichtung eines neuen Benutzers wird die Login-Shell mit angegeben und in der Datei /etc/passwd abgespeichert. Möchten Sie die Login-Shell eines Benutzers ändern, können Sie dies entweder direkt in dieser Datei eintragen oder über das Distributions-Tool (z.B. YaST bei SuSE-Systemen) modifizieren. Im laufenden Betrieb einer Shell kann durch Starten einer anderen Shell die Shell gewechselt werden. Wie schon erwähnt, sind die Shells eigenständige Programme und können wie jedes andere Programm gestartet werden, mit Options- und Parameterübergabe. Z.B. wechselt der Aufruf von zsh in die Z-Shell und der Aufruf von exit beendet diese Shell wieder.
Shell-Skripte sind Text-Dateien, die eine Folge von Shell-Befehlen enthalten (ähnlich den Batch-Dateien unter MS-DOS). Hierfür gibt es zwei Aufrufvarianten. Entweder über
<Shell-Name> [<Optionen>] <Skript-Name und Pfad> [<Parameter>]
wobei das Skript mit der angegebenen Shell gestartet wird. Hier können auch spezielle Shell-Optionen mit angegeben werden und die Parameter werden dem Skript übergeben. Die etwas gebräuchlichere Aufrufsyntax lautet aber:
<Skript-Name und Pfad> [<Parameter>]
Hierbei muß aber die Skript-Datei als ausführbar markiert werden. Dem Skript werden die angegebenen Parameter übergeben und in einer Kind-Shell der aktuellen Shell gestartet. Enthält das Shell-Skript Befehle und Syntax einer speziellen Shell, kann über Angabe in der ersen Zeile des Skripts die zu benutzende Shell festgelegt werden. Die Form sieht folgendermaßen aus:
#!/bin/ksh
um z.B. das Skript in einer Korn-Shell ablaufen zu lassen. Somit stehen Ihnen die Möglichkeiten offen, für jedes Skript die geeignete Shell auszuwählen und den effizientesten Code zu programmieren. Die grundlegende Bedienung der Kommandozeile finden Sie in Kapitel “Arbeiten auf der Kommandozeile“ und die Konfigurations-Dateien in Kapitel “Konfigurationsdateien“.
Bevor wir mit dem Arbeiten in einer Shell beginnen, müssen zuerst einige Begriffe geklärt werden:
ist eine Eingabezeile, die von der Shell zu interpretieren ist.
sind Zeichen (keine Ziffern oder Buchstaben), denen von der entsprechenden Shell eine Sonderbedeutung zugeordnet ist.
sind Leer- oder Tabulatorzeichen.
ist eine Folge von Buchstaben (keine Umlaute oder ß), Ziffern oder Unterstrichen, wobei diese Folge mit einem Buchstaben oder einem Unterstrich beginnen muß. Bezeichner werden z.B. für Namen von Shell-Variablen oder Shell-Funktionen verwendet. Bezeichner wie
vier_*_hotel 2A 7_und_40_elf kündigungsind also nicht erlaubt.
ist eine Folge von Zeichen, die durch ein oder mehrere der folgenden Metazeichen
; & ( ) | < > Neuezeile-Zeichen Leerzeichen Tabulatorzeichen
abgegrenzt ist; mit \ ausgeschaltete Metazeichen (wie z.B. \;) sind allerdings Teil eines Wortes.
Einige Jokerzeichen werden Sie schon von MS-DOS oder anderen Betriebssystemen her kennen, andere werden neu für Sie sein. Die wohl bekanntesten Jokerzeichen sind * und ?, welche unter Linux null oder beliebig viele Zeichen bzw. genau ein Zeichen abdecken. Dabei wertet die Shell zuerst die Jokerzeichen in einer Kommandozeile aus und expandiert diese, bevor sie an das angegebene Kommando übergeben werden. Z.B. wird mit dem Aufruf von
ls -l a*
zuerst a* expandiert und anschließend die komplette Liste an den Befehl ls übergeben. Der eigentliche Aufruf von ls könnte dann so aussehen:
ls -l amplitude.gif anhang1.tex axinom.tex
In den Shells gibt es zur verfeinerten Auswahl noch weitere Jokerzeichen (sie Übersichts-Tabelle). Die Angabe von [abc] sucht nach genau eines der angegebenen Zeichen, Bereichsangaben können über [a-f] angegeben werden. Soll keines der Zeichen vorhanden sein, wird ein Ausrufezeichen vorangestellt [!abc]. Die Tilde ist ein Alias-Name für das Heimatverzeichnis des Benutzers, wenn sie als Pfadname angegeben wird, ~ logname bezeichnet das Heimatverzeichnis des Benutzers logname. Ansonsten kennzeichnet eine Tilde häufig Backup-Dateien. Ein Wort kann dabei auch mehrere Jokerzeichen enthalten, z.B.
ls -l [a-f]*.[tl][ex]x
Hierbei werden alle Dateien angezeigt, die mit a-f beginnen, dann beliebig viele Zeichen (auch null) enthalten, gefolgt von einem Punkt und dem Suffix tex oder lyx enden. Wenn Sie aber versuchen sollten, mehrere Dateien gleichzeitig umzubenennen wird Ihnen das nicht gelingen. Der Aufruf von
mv *.x *.y
um alle Dateien mit Suffix .x zum Suffix .y umzubenennen wird mit einer Fehlermeldung abbrechen. Sie können sich vielleicht schon denken, warum das so ist. Zuerst ersetzt die Shell *.x durch alle Dateien mit diesem Suffix, für *.y wird jedoch keine Datei gefunden und der Parameter so an mv übergeben. mv kann aber nicht mehrere Dateien gleichzeitig umbenennen (nur verschieben) und bricht mit einer Fehlermeldung ab. Hierfür muß also eine andere Lösung gefunden werden (z.B. eine Schleife).
Ein anderes Problem entsteht z.B. beim Befehl ls, wenn durch das Pattern ein Verzeichnis mit abgedeckt wird. Hier wird nicht nur der Verzeichnisname ausgegeben, sondern auch der Inhalt des Verzeichnisses (was vielleicht gar nicht erwünscht ist). Gibt man ls die Option -d mit an, werden nur die Verzeichnisnamen und nicht deren Inhalt angezeigt.
In manchen Anwendungsfällen kann es nützlich sein, die Ausgabe eines Kommandos als Teil der Kommandozeile interpretieren zu lassen. Dazu bietet die Shell die sogenannten Substitutionsmechanismen an. Kommandos werden in Metazeichen ' ', “ “, ` ` oder $() geklammert, die verschiedene Arten des Substitutionsmechanismus erlauben. Dabei muß zwischen Variablensubstitution und Kommandosubstitution unterschieden werden. Z.B. möchten Sie den String „Heute ist der “ gefolgt von dem aktuellen Datum ausgeben. Die Kommandozeile
echo Heute ist der date '+%d.%m.%y (%a)'
führt nicht zu der gewünschten Ausgabe, da nicht date durch sein Ergebnis substituiert wird. Wird jedoch
echo Heute ist der ` date '+%d.%m.%y (%a)'`
eingegeben, wird das in Gegen-Apostrophen geklammerte Kommando zuerst ausgeführt und das Ergebnis im echo-Kommando eingesetzt. Der daraus resultierende Befehl lautet:
echo Heute ist der 23.07.00 (Sun)
Wird vor dem Substitutionszeichen, ebenso bei allen anderen Metazeichen, ein Backslash angegeben, so hebt dieser die Sonderbedeutung auf. In der Tabelle Übersicht der Substitutionsmetazeichen sehen Sie die vier Substitutionsmetazeichen und welche Substitutionsmechanismen darin möglich sind.
Manchmal ist es notwendig, die Standardeingabe und -ausgabe umzulenken. Die üblichen Voreinstellungen sind dabei:
Dabei wird zwischen Standardausgabe und -fehlerausgabe unterschieden, um echte Daten von Fehler- oder Diagnosemeldungen zu trennen. Um nun diese zu ändern, werden u.a. die Zeichen < und > benötigt (siehe Tabelle Übersicht über alle Umleitungssonderzeichen). Der Eingabestrom kann über <datei und der Ausgabestrom über >datei jeweils von / in eine Datei umgelenkt werden. Dabei können Umlenkungsanweisungen an beliebiger Stelle in einem einfachen Kommando angegeben werden. Die nachfolgenden Kommandozeilen sind identisch, aus Gründen der Lesbarkeit sollte aber die Form (1) oder (2) bevorzugt werden.
cat <ein >aus (1) cat >aus <ein (2) <ein cat >aus (3) <ein >aus cat (4) >aus cat <ein (5) >aus <ein cat (6)
Werden zwei Kommandos über eine Pipe (|-Zeichen) verbunden, so leitet das Kommando 1 seine Ausgabe an Kommando 2 weiter. Die wohl bekannteste Form der Pipe ist die Ausgabe von Text, der mittels des Befehls more seitenweise angezeigt wird.
cat text | more
Hierbei wird zuerst die Datei text von cat ausgegeben und diese Standardausgabe an den Befehl more als Standardeingabe weitergereicht, der dann die seitenweise Ausgabe der Datei text vornimmt. Manchmal macht der Unterschied zwischen einer Pipe und der Kommandosubstitution anfänglich Schwierigkeiten. Das nachfolgende Beispiel soll diesen Unterschied helfen zu verdeutlichen. Eine Datei zaehle.txt soll Namen von Dateien enthalten, zu denen die darin enthaltene Wortzahl zu bestimmen ist:
tobias@master # cat zaehle.txt /etc/magic /etc/inittab /usr/include/stdio.h tobias@master #
Der Aufruf von
tobias@master # cat zaehle.txt | wc -w 3
liefert dann die falsche Ausgabe, da in diesem Fall nicht der Inhalt der in zaehle.txt genannten Dateien, sondern der Inahlt von zaehle.txt selbst ausgewertet wird. Mit dem Aufruf
tobias@master # wc -w %%`cat zaehle.txt`%%
dagegen wird - wie gefordert - der Inhalt der in zaehle.txt angegebenen Dateien und nicht der Inhalt von zaehle.txt selbst ausgewertet. Nach der Durchführung der Kommandosubstitution `cat zaehle.txt` würde folgende Kommandozeile aus der obigen resultieren:
wc -w /etc/magic /etc/inittab /usr/include/stdio.h
und z.B. folgende Ausgabe liefern:
572 /etc/magic 112 /etc/inittab 365 /usr/include/stdio.h 1049 total
Gelegentlich möchten Sie vielleicht auch die Ausgabe eines Programmes am Bildschirm betrachten und gleichzeitig in eine Datei schreiben. Für dieses Problem steht Ihnen das Programm tee zur Verfügung, welches den Ausgabestrom verdoppelt und die parallele Ausgabe auf zwei Ausgabeströme ermöglicht. Dabei wird dem Programm tee die Ausgabe eines Kommandos über eine Pipe als Eingabe übergeben.
<Kommando> | tee <Dateiname>
Die nachfolgende Kommandozeile soll den Gebrauch von tee demonstrieren. In diesem Beispiel wird mittels ls -l der Inhalt des aktuellen Verzeichnisses einmal in die Datei inhalt1 gespeichert, der zweite Ausgabestrom wird über eine weitere Pipe an das Kommando sort weitergegeben, dort nach der Dateigröße (fünfte Spalte, also Option +4) sortiert und in inhalt2 gespeichert.
ls -l | tee inhalt1 | sort +4 > inhalt2
Normalerweise läuft eine Kommandoausführung so ab, wobei Sie das Kommando eingeben, dann von der Shell interpretiert und ausgeführt wird und anschließend die Ausgabe auf dem Bildschirm erfolgt und die Shell für ein neues Kommando bereitsteht. In den vorherigen Abschnitten haben Sie die Möglichkeiten kennengelernt, mehrere Kommandos gleichzeitig einzugeben und ausführen zu lassen. Für die weitere Steuerung der Kommandoausführung gibt es Sonderzeichen, womit Sie, ohne Umleitung der Ausgaben, mehrere Kommandos einer Kommandozeile eingeben können und diese sogar nur bedingt ausgeführt werden.
Werden zwei Kommandos mit einem Semikolon getrennt, wird zuerst das Kommando 1 ausgeführt und bei Beendigung das Kommando 2. Soll das Kommando 2 nur dann ausgeführt werden, wenn Kommando 1 erfolgreich ablief, können Sie die Kommandos mit && verknüpfen. Für das Gegenteil gibt es die Verknüpfung || (doppeltes Pipe-Symbol), also nur wenn Kommando 1 fehlerhaft ablief, soll Kommando 2 gestartet werden.
Da Linux ein Multitasking-System ist, können Kommandos auch im Hintergrund gestartet werden, so daß die Shell nicht auf die Beendigung des Kommandos wartet sondern gleich wieder zur Eingabe bereit steht. Sie sollten aber darauf achten, daß Ein- und Ausgaben trotzdem auf dem Bildschirm erscheinen und somit möglicherweise zu einer Vermischung der Ein- und Ausgaben des Vorder- und Hintergrundprozesses kommen kann. Hier sollten Sie die Möglichkeiten der Umleitung benutzen. Ein Programm kann man über Angabe des Kaufmanns-UND am Ende des Kommandos im Hintergrund starten:
kommando &
Hierbei wird dann noch die PID-Nummer ausgegeben, die für verschiedene Programme, z.B. kill, benötigt wird. Sollten Sie vergessen haben, das Kommando als Hintergrundprozeß zu starten, so können Sie jederzeit das Hintergrundprogramm über die Tastenkombination STRG+Z stoppen und durch Eingabe des Befehls bg im Hintergrund fortführen. Also brauchen Sie nicht das Kommando mittels STRG+Strg brutal zu stoppen und neu zu starten. Werden Kommandos in runden Klammern ( ) gefaßt, führt die Shell die Kommandos in derselben Shell aus und liefen ein gemeinsammes Ergebnis (ansonsten wird für jedes Kommando eine neue Sub-Shell gestartet). Z.B. durch
tobias@master # (ls ; date) > inhalt
wird in die Datei inhalt eine Dateiliste sowie das aktuelle Datum geschrieben. In Tabelle Übersicht der Kommandoausführungssonderzeichen finden Sie eine Übersicht zu den Sonderzeichen der Kommandoausführung.
Sie standen vielleicht schon mal vor dem Problem, viele ähnliche Verzeichnisse zu erstellen, z.B. teil1, teil2, teil3… Die bash kann Ihnen hierfür den Tippaufwand abnehmen, da sie aus Zeichenketten, die in geschweiften Klammern angegeben sind, alle denkbaren Zeichenkettenkombinationen zusammenstellt. Die einzelnen Zeichenketten werden durch Kommatas getrennt. Der Vorteil gegenüber Jokerzeichen besteht darin, daß auch nicht existierende Dateinamen gebildet werden können, z.B. für mkdir.
tobias@master # echo {a,b}{1,2,3}
a1 a2 a3 b1 b2 b3
Die bash kann auch ganzahlige arithmetische Ausdrücke mit 32-Bit-Integerzahlen (Zahlenbereich zwischen +/- 2147483648) berechnen. Hierfür müssen Sie den Ausdruck in eckige Klammern stellen und ein $-Zeichen voranstellen.
tobias@master # echo $[100+45] 145
Als Operatoren sind die meisten aus der Programmiersprache C bekannten erlaubt: + - * / für die vier Grundrechenarten, % für Modulo-Berechnungen, == != < <= > >= für Vergleiche, << und >> für Bitverschiebungen, ! && || für logisches NICHT, UND und ODER etc. Wenn einzelne Werte aus Variablen entnommen werden sollen, muß ein $-Zeichen vorangestellt werden.
Alternativ und bei Shells, die keine Berechnungen durchführen können, kann das Komando expr verwendet werden.
tobias@master # expr 100 + 45 145
Viele Einstellungen der bash und anderer Anwendungsprogramme werden über Shell-Variablen gesteuert. Bei den Variablen muß unterschieden werden zwischen globalen, lokalen und dynamisch-veränderlichen. Beim Systemstart werden schon viele Variablen gesetzt, die Sie entweder über Konfigurationsdateien oder im laufenden Betrieb verändern können (z.B. über die Datei /etc/profile). Andere verändern sich im laufenden Betrieb dynamisch, so z.B. die Variable PWD, die immer den aktuellen Pfad enthält. Die Zuweisung einer Variablen erfolgt durch den Zuweisungsoperator =:
var = <wert>
Soll der Inhalt von der Variablen Leerzeichen oder andere Sonderzeichen enthalten, muß die Zeichenkette in einfache oder doppelte Hochkommata gestellt werden. Bei der Zuweisung können mehrere Zeichenketten unmittelbar aneinandergereiht werden und die Kommandosubstitution vorgenommen werden. Beachten Sie, daß alle durch einfache Zuweisung entstandenen Variablen als lokale angelegt werden. Beim Beenden der Shell (oder der Sub-Shell) gehen diese verloren. Soll eine lokale Variable als globale makiert werden, muß export <var> oder declare -x <var> aufgerufen werden. Die Tabelle Kommandos zur Variablenverwaltung gibt die verschiedenen Kommandos zur Variablenverwaltung wieder, dabei gibt es mehrere funktionelle Überlappungen.
Zu beachten ist noch, daß Variablenzuweisungen, auch wenn sie global sind, nur für eine Shell gelten. Arbeiten Sie auf mehreren Terminals, so laufen dort voneinander unabhänginge Shells. Oft benötigte Variablen können Sie in den Konfigurationsdateien eintragen und somit bei jedem Systemstart verfügbar halten.
Sie sollten darauf achten, daß Sie nicht vorhandene System-Variablen überschreiben, die für den Betrieb der Shell und anderer Anwendungsprogramme notwendig sind. Die Tabelle System-Variablen beschreibt die wichtigsten System-Variablen.
Desweiteren gibt es noch einige Variblen, die durch die Shell vordefiniert sind und sich dynamisch ändern. Diese werden vor allem in Shell-Skripten verwendet. Die Tabelle vordefinierte dynamisch-änderliche Shell-Variablen zeigt die Bedeutung dieser Variablen.
Die Shell unterscheidet bei Variablen zwischen undefinierten Variablen und Variablen, denen explizit der „Nullwert“ (leere Zeichenkette) zugewiesen wurde. Dieses Vorgehen ist gerade bei der Fehlersuche in Skripten wichtig und kann über verschiedene Methoden festgestellt werden (z.B. über die Parametersubstitution, s.u.).
Mit dem bash-Kommando read können Sie während des Ablaufs eines Shell-Skripts Eingaben vornehmen und in Variablen speichern. Wird die Option -n angegeben, erfolgt kein Wechsel in eine neue Zeile, bevor die Werte eingegeben werden können. Die Aufruf-Syntax lautet:
read [Optionen] <var>
Unter diesem Namen stellt die bash einige Kommandos zur Verfügung, die die Bearbeitung von Zeichenketten in Variablen ermöglichen. Die Syntax hierfür lautet ${var__muster}, wobei __ für ein bis zwei Sonderzeichen steht, die die Bearbeitung näher spezifizieren. Zu beachten ist, daß die Variablen ohne vorangestelltes Dollarzeichen ($) angesprochen werden, außer wenn Vergleichsmuster aus einer Variablen gelesen werden sollen. In Tabelle Übersicht der Parametersubstitutions-Syntax sind die Mechanismen einzelnd aufgeführt.
Bevor wir auf die einzelnen Programmiersprachenkonstrukte eingehen, muß noch die Auswertung von Ausdrücken angesprochen werden, da sie als Bedingungen für die Konstrukte notwendig sind. Hierfür gibt es zwei Kommandos, test um Bedingungen zu prüfen und expr um einfache arithmetische Berechnungen durchzuführen. Zu beachten ist, daß die Shells bzw. die Anwendungsprogramme bei einer erfolgreichen Ausführung, anders als in den Programmiersprachen, einen exit-Wert von 0 und bei einem Fehler einen exit-Wert unterschiedlich von 0 liefern. Das gleiche gilt für die Wahrheitswerte true (= 0) und false (<> 0).
Das builtin-Kommando test formuliert eine Vielzahl von Bedinungen über Optionen, die in den Tabellen test-Bedingungen auf Zeichenketten, test-Bedingungen auf Zahlen, test-Bedingungen auf Dateien und verknüpfte test-Bedingungen aufgelistet werden3). Ist die Bedingung wahr, so liefert test einen exit-Wert von 0, ansonsten einen von 0 unterschiedlichen Wert. Sollten keine Argumente test übergeben werden, so liefert test immer einen Wert unterschiedlich von 0 (falsch). Für den Aufruf gibt es zwei Möglichkeiten:
test <ausdruck>
oder
[ ausdruck ]
Wichtig ist bei der zweiten Form, daß nach [ und vor ] mindestens ein Leer- oder Tabulatorzeichen angegeben wird. Sollen Zeichenketten mit Leerzeichen verglichen werden, müssen diese in Hochkommata gestellt werden, damit der komplette String verglichen wird. Gerade bei Variablen kann dies eine Fehlerquelle sein.
Dieses Kommando kann zur Berechnung von arithmetischen Ausdrücken (siehe auch Kapitel Berechnung arithmetischer Ausdrücke), zum Vergleich zweier Zeichenketten etc. benutzt werden. Die Aufrufsyntax lautet:
expr argument(e)
Als Argumente können Zeichenketten, ganze Zahlen oder Konstruktionen, die eine ganze Zahl oder Zeichenketten liefern, wie Variablenwerte oder Kommandosubstitution, angegeben werden. Folgendes ist dabei zu beachten:
Nachfolgend in Tabelle Übersicht der expr-Operatoren werden die einzelnen Operatoren beschrieben (in der Reihenfolge ihrer Prioritäten).
| Tabelle: Übersicht der expr-Operatoren<BOOKMARK:tab_ueberischt_der_expr-operatoren> | |
| Operator | Bedeutung |
|---|---|
| op1 : op2 | überprüft, ob der reguläre Ausdruck des Operanden op2 den Operanden op1 abdeckt;liefert die Anzahl der abgedeckten Zeichen, sonst 0; soll der abgedeckte Teil selbst ausgegeben werden,so ist der entsprechende reguläre Ausdruck in op2 mit \(…\) zu klammern; |
| op1 \* op2 | Multipliziert op1 mit op2 |
| op1 / op2 | Dividiert op1 durch op2 und liefert ein ganzzahliges Ergebnis |
| op1 % op2 | Liefert den Rest aus der Ganzzahldivision von op1 durch op2 (Modulooperator) |
| op1 + op2 | Addiert op1 mit op2 |
| op1 - op2 | Subtrahiert op2 von op1 |
| op1 = op2 | prüft, ob op1 gleich op2 ist |
| op1 \> op2 | prüft, ob op1 größer als op2 ist |
| op1 \>= op2 | prüft, ob op1 größer oder gleich op2 ist |
| op1 \< op2 | prüft, ob op1 kleiner als op2 ist |
| op2 \<= op2 | prüft, ob op1 kleiner oder gleich op2 ist |
| op1 != op2 | prüft, ob op1 ungleich op2 ist |
| op1 \& op2 | liefert als Ergebnis op1, wenn keiner der beiden Operatoren op1 oder op2 der Wert 0 oder die leere Zeichenkette ist (nicht zu verwechseln mit einer undefinierten Variablen), ansonsten wird als Ergebnis 0 geliefert; |
| op1 \| op2 | liefert als Ergebnis op1, wenn der Operand op1 nicht der Wert 0 oder die leere Zeichenkette ist, ansonsten wird als Ergebnis op2 geliefert |
Die if-Anweisung ist eine Verzweigungsanweisung, wobei zuerst der exit-Status des letzten Kommandos in der Bedingung überprüft wird. Liefert dieser einen exit-Status von 0, wird die kdoliste ausgeführt, ansonsten diese einfach übersprungen. Hierbei muß zwischen der einseitigen Auswahl (if ohne else-Teil), der zweiseitigen Auswahl (if mit else-Teil) und der Mehrfach-Auswahl (if mit elif-Teil) unterschieden werden.
Syntax
if Bedingung1 then then_kdoliste1 [elif Bedingung2 then then_kdoliste2] [else else_kdoliste1] fi
Beispiel für eine einseitige Auswahl:
if test $# -eq 0 then echo "Ein Argument muss mindestens angegeben werden" exit 1 fi
In diesem Beispiel wird überprüft, ob beim Starten des Shell-Skripts mindestens ein Parameter angegeben wurde. Ist dies nicht der Fall, so ist die Bedingung wahr, der then-Teil wird ausgeführt und eine Meldung ausgegeben sowie das Skript abgebrochen. Ansonsten wird nach dem Schlüsselwort fi fortgefahren.
Beispiel für eine zweiseitige Auswahl:
if test $# -eq 0 then echo "Es wurde kein Argument angegeben" else echo "Es wurden die Argumente " $* " angegeben" fi
Dieses Skript funktioniert ähnlich wie das vorherige. Zuerst wird überprüft, ob Parameter dem Skript übergeben wurde. Ist dies nicht der Fall, so ist die Bedingung wahr und es wird der then-Teil ausgeführt. Ansonsten wird der else-Teil ausgeführt und alle Parameter ausgegeben. Anschließen wird nach dem Schlüsselwort fi fortgefahren.
Beispiel für die Mehrfach-Auswahl:
if [ "$1" = "gruen" ] then echo "Fahren" elif [ "$1" = "gelb" ] then echo "Wenn rot -> gelb: Vorsichtig fahren" echo "Wenn gruen -> gelb: Stoppen" elif [ "$1" = "rot" ] then echo "Stoppen" else echo "Ampel ausser Betrieb" fi
Dieses Shell-Skript gibt für eine Ampelfarbe die erforderliche Reaktion eines Autofahrers aus (aus [Herold, Seite 119]). Dabei wird die Ampelfarbe als erstes Argument übergeben und über die if- und elif-Anweisungen ausgewertet. Sollte kein Argument oder eine falsche Zeichenfolge angegeben werden, so wird der else-Teil ausgeführt.
Die case-Anweisung eignet sich besser für die Mehrfach-Auswahl, z.B. um Menüs zu realisieren, als die if-Anweisungen. Dabei wird die Zeichenkette wort in der angegebenen Reihenfolge zunächst mit pattern1, dann mit pattern2… verglichen, bis eine Übereinstimmung gefunden wird. Bei einer Übereinstimmung wird die zugehörige kdoliste ausgeführt und anschließend nach dem Schlüsselwort esac fortgefahren. Das Zeichen ) trennt die pattern-Angabe von der zugehörigen kdoliste und achten Sie auf die beiden Semikla am Ende der kdoliste, sie müssen angegeben werden. Bei den Pattern können Sie die Substitutionsmechanismen benutzen, / oder der führende Punkt oder ein Punkt, welcher unmittelbar / folgt, müssen nicht explizit wie bei der üblichen Dateinamenexpandierung in wort angegeben sein.
Den Substitutionsmechanismus wird häufig auch für den Default-Zweig gewählt in der Form *) bzw. ?). Achten Sie aber darauf, diesen als letzten anzugeben, da der Reihenfolge nach verglichen wird und sonst, wenn Sie diesen womöglich als ersten Pattern angeben, immer nur dieser ausgeführt wird.
Syntax
case wort in pattern1) kdoliste1;; pattern2) kdoliste2;; : : patternn) kdolisten;; esac
Beispiel für eine case-Auswahl
case "$Menuitem" in 1) echo "Menüpunkt 1 wird ausgeführt";; 2) echo "Menüpunkt 2 wird ausgeführt";; [EeXx]) echo "Menü wird beendet";; *) echo "Falsche Eingabe";; esac
Befindet sich in der Variablen Menuitem der Wert 1 bzw. 2, werden die entsprechenden Kommandolisten ausgegeben. Sollte der Wert ein E, e, X oder x sein, so wird die Meldung ausgegeben, daß das Menü beendet wird. Befindet sich ein anderer Wert in der Variablen, so wird die Meldung „Falsche Eingabe“ ausgegeben.
Mit der while-Schleife lassen sich Kommandos abhängig von der Bedingung wiederholt ausführen. Wenn der exit-Status des letzten Kommandos in der kdoliste1 gleich 0 (erfolgreich) ist, wird die kdoliste2 ausgeführt. Dieser Ablauf wiederholt sich so lange, bis die kdoliste1 einen exit-Status unterschiedlich von 0 liefert. Dann wird hinter dem done-Schlüsselwort die Bearbeitung weitergeführt. Natürlich kann es dabei passieren, daß die kdoliste2 gar nicht ausgeführt wird, wenn schon die erste Ausführung der kdoliste1 einen exit-Wert unterschiedlich von 0 liefert.
Syntax
while kdoliste1 do kdoliste2 done
Beispiel für eine while-Schleife
while who | grep $1 >> /dev/null do echo "Benutzer $1 ist immer noch angemeldet" sleep 300 done echo "Benutzer $1 hat sich abgemeldet"
Dieses Skript prüft alle 300 Sekunden, ob der Benutzer, der als Parameter übergeben wurde, noch auf dem System angemeldet ist und gibt dann eine Meldung aus. Die Ausgabe der kdoliste1 wird auf /dev/null (einem großen, schwarzen Loch) umgeleitet, da nicht die Ausgabe, sondern nur der exit-Wert relevant ist.
Die until-Schleife funktioniert ähnlich wie die while-Schleife, sie ist die Umkehrung dazu. Der Schleifenrumpf (kdoliste2) wird so lange ausgeführt wie kdoliste1 einen exit-Wert verschieden von 0 liefert. Wie auch schon bei der while-Schleife kann es auch hier passieren, daß der Schleifenrumpf nicht durchlaufen wird, und zwar dann, wenn die kdoliste1 beim ersten Ausführen sofort einen exit-Status von 0 liefert.
Syntax
until kdoliste1 do kdoliste2 done
Beispiel für eine until-Schleife
until who | grep $1 >> /dev/null do echo "Benutzer $1 ist noch nicht angemeldet" sleep 300 done echo "Benutzer $1 hat sich angemeldet"
Dieses Beispiel ist die Umkehrung des Beispiels zur while-Schleife, wobei alle alle 300 Sekunden geprüft wird, ob sich der als Parameter übergebene Benutzer noch nicht angemeldet hat. Die Schleife wird so lange durchgeführt, bis sich der Benutzer angemeldet hat.
Anders als in den beiden vorherigen Schleifen, wo die Durchführung des Schleifenrumpfes an eine Bedinung geknüpft ist, wird bei der for-Schleife die Anzahl der Schleifendurchläufe über eine wort-Liste festgelegt. Hierfür gibt es zwei Möglichkeiten, entweder implizit alle Positionsparameter (keine in-Angabe) oder alle danach angegebenen worte (bei einer in-Angabe). Der Laufvariablen wird dabei nacheinander jedes einzelne wort zugewiesen.
Syntax
for laufvariable [in wort1 wortn] do kdoliste done
Beispiel für eine for-Schleife ohne in-Angabe
nummer=1 for i do echo "$nummer. Argument: $j" nummer=` expr $nummer + 1` done
Bei diesem Skript werden alle übergebenen Parameter nacheinander ausgegeben, vorangestellt wird die Zeichenfolge “<Argumentnummer>. Argument:“.
Beispiel für eine for-Schleife mit in-Angabe
for i in * do cat $i chmod 755 $i done
Dieses Skript gibt nacheinander alle Dateien im aktuellen Verzeichnis mit cat aus (außer die, deren Name mit . beginnt) und ändert die Zugriffsmaske auf “rwxr-xr-x“.
break bewirkt das unmittelbare Verlassen der direkt umgebenen Schleife (while, until und for). Dabei kann break mit einem optionalen Zahlenwert n aufgerufen werden. Dieser Zahlenwert legt dann die Schachtelungstiefe fest, welche verlassen werden soll. Ohne Angabe dieses Zahlenwertes wird die aktuelle Schleife abgebrochen und die Ausführung nach dem Schleifenrumpf fortgesetzt. Häufig ist die Ausführung von break an eine Bedingung mittels if- oder case-Konstrukt verknüpft.
Syntax:
break [n]
Der Aufruf des Kommandos continue in einem Schleifen-Körper (while, until und for) bewirkt die unmittelbare Ausführung des nächsten Schleifendurchlaufs. Es wird nicht wie bei break die Schleife verlassen, sondern sofort zur Schleifenbedingung gesprungen und dort fortgefahren. continue kann ein optionaler Zahlenwert n angegeben werden, der die Anzahl der Schachtelungstiefe festlegt, die übersprungen werden soll - ansonsten wird für die direkt umschließende Schleife neu geprüft und durchlaufen. Üblich ist der Gebrauch von continue-Anweisungen in Verbindung mit if- oder case-Konstruktionen in Schleifen.
Syntax:
continue [n]
Das Kommando exit beendet unmittelbar das Shell-Skript bzw. die aktive Shell. Wird exit ohne optionales Argument (Zahlenwert) aufgerufen, liefert es den exit-Status 0 zurück, ansonsten den Zahlenwert. Dieser kann vor allem zur Fehlersuche benutzt und ausgewertet werden, da der Rückgabewert des letzten Kommandos (hier dann also exit) über die Variable $? ausgelesen werden kann.
Syntax:
exit [n]
Wie in höheren Programmiersprachen ist es in der bash erlaubt, Funktionen zu definieren. Dabei muß zwischen { und dem ersten Kommando aus kdoliste mindestens ein Leerzeichen, Tabulatorzeichen oder Neuezeile-Zeichen angegeben sein. Zwischen dem letzten Kommando aus kdoliste und dem abschließenden } muß immer ein Semikolon angegeben sein, wenn sich diese beiden in derselben Zeile befinden. Anschließend kann die Funktion über ihren Namen aufgerufen (ohne Klammern) und Parameter mit übergeben werden, die dann der entsprechenden Funktion in Form von Positionsparametern zur Verfügung gestellt werden. Der Parameter $0 enthält dann allerdings nicht den Funktionsnamen, sondern den Namen der ausführenden Shell. Der Bezeichner function bei der Funktionsdefinition ist optional, verhilft aber zur besseren Lesbarkeit des Skripts. Über das Variablendefinitionskommando local können Variablen definiert werden, die nur innerhalb der Funktion gültig sind. Das Schlüsselwort return bewirkt das Verlassen der Funktion.
Syntax:
[function] funktionsname() { kdoliste;}
Beispiel:
funktion ll() { ls -CF "$@" echo "---\n` ls "$@" | wc -l` Dateien";}
Hier wird eine neue Funktion ll definiert, die alle Dateien bzw. die über Parameter angegebenen Dateien eines Verzeichnisses mit der Opton -CF listet und anschließend eine Trennlinie sowie die Anzahl der Dateien ausgibt. Die Funktion kann dann über
tobias@master # ll
aufgerufen werden, um alle Dateien aufzulisten oder beispielsweise mittels
tobias@master # ll a*
aufgerufen werden, um alle Dateien, die mit „a“ beginnen, aufzulisten.