C-KURS WS 01/02 - Theorie 1 - Einfuehrung


AUTHOR: Gerd Döben-Henisch
DATE OF FIRST GENERATION: Sept 7, 2001
DATE OF LAST CHANGE: Sept 27, 2001
EMAIL: Gerd Döben-Henisch



Software-Engineering

Das Endprodukt einer Programmiertätigkeit ist im Erfolgsfall der Objectcode, der in einer EDV-Anlage (Computer) lauffähig ist.

Ausgangspunkt ist im Normalfall ein Anwendungsproblem, für das mit den Mitteln der Informatik eine Lösung erarbeitet werden soll. Es ist Aufgabe des Ingenieurs bzw. des Informatikers, dieses Anwendungsproblem in Zusammenarbeit mit den Auftraggebern soweit zu konkretisieren, dass es in einer natürlichsprachlichen Formulierung als Anforderungskatalog vorliegt.

Auf der Basis eines solchen Anforderungskataloges kann man dann das Anwendungsproblem weiter bearbeiten. Im Prinzip geht es dabei immer um Übersetzungsprozesse in solche symbolische Repräsentationen, die im allgemeinen Fall eine unbeschränkt mathematische, im eingeschränkteren Fall einer berechenbaren, sprich algorithmischen Darstellung entsprechen.

Für die Erstellung algorithmischer Repräsentationen bedient man sich heute mehr und mehr sogenannter Case Tools; das sind Softwareprogramme, die es dem Softwareingenieur ermöglichen, bestimmte Darstellungsweisen wie z.B. Strukturierte Analyse (SA), Entity Relationsship (ER), Universal Modelling Language (UML), Nassi Shneiderhan (SN) usf. computergestützt zu generieren. Für grössere Softwareprojekte ist der Einsatz solcher Werkzeuge eigentlich ein 'Muss'.

Die meisten Case Tools bieten zusätzlich die Möglichkeit, die erstellten symbolischen Repräsentationen mittels sogenannter Converter in einem weiteren Schritt direkt in den Source-Code bestimmter Programmiersprachen zu übersetzen (z.B. in C, C++, Java, Cobol, usw.).

Im Rahmen des Kurses Programmieren I werden noch keine Case Tools benutzt. Um in einen 'direkten Kontakt mit der Programmiersprache zu kommen, wird der Source-Code 'per Hand' mittels eines Editors erstellt. Als Standard-Editor werden wir xemacs benutzen.

Der Source-Code wird dann mit einem Compiler in den Object-Code übersetzt, der von dem Zielcomputer 'verstanden' wird. Object-Code ist bei heutigen Prozessoren ( CPUs := Central Processing Units) immer Binär-Code. Als Compiler werden wie den GNU-C Compiler gcc benutzen.

Für den Kurs Programmieren I benutzen wir als Betriebssystem (OS := Operating System) Linux. Um direkt mit dem Betriebssystem kommunizieren zu können, gibt es für Linux verschiedene Befehls-Schnittstellen, sogenannte Shells. Die Standard-Shell ist die Bash-Shell. Diese wird im Kurs benutzt werden.

Sämtliche Tätigkeiten, angefangen von der Problemerfassung bis hin dann zur endgültigen Objekt-Code-Erstellung fallen unter das Oberthema Software-Engineering.

Im Umkreis einer EDV-Anlage gibt es heute zahlreiche spezielle Ausprägungen wie z.B. Echtzeit oder nicht Echtzeit, Parallelverarbeitung, verteilte Anwendungen verbunden mit Bus Systemen oder Netzwerken, Client/Server-Strukturen usw. Im Kurs Programmieren I wird nur angenommen, dass es ein 'normales' Betriebssystem gibt, auf dem verschiedene Programme laufen, die über Dateien miteinander kommunizieren.

START




Prozesse

Die Shell (in unserem Fall die Bash-Shell) (Bash für Windows) ist ein Programm, das unter dem Betriebssystem Linux als ein permanenter Prozess läuft. Sobald ein Benutzer sich anmeldet ('einlogged'), wird für ihn eine Shell als Prozess mit eigener ID gestartet. Bei der Abmeldung wird der Prozess wieder gestoppt.

Während eine Shell läuft, nimmt sie Befehle entgegen. Entweder direkt über die Befehlszeile ('command line') oder aus einer Datei.

Einzelne Befehle haben das allgemeine Format: BEFEHL [OPTIONen] [[ARGUMENT_1] ARGUMENT_n] .... Will man Details zu einem Befehl nachschauen, dann kann man dies mit dem Befehl man Befehlsname tun.

Sie können einzeln oder als Folgen eingegeben werden.

Mit dem Befehl ps -Al kann man Prozesse auflisten, die aktuell laufen. Die Option '-Al' besagt, dass 'alle' aufgeführt werden sollen und zwar im 'Langformat'. In dieser Auflistung hat jeder Prozess eine Prozess-Nummer ('process ID = PID'). Will man einen dieser Prozesse gezielt stoppen, dann kann man dies mit dem Befehl 'kill' tun: kill PID.

Jeder laufende Shell-Prozess hat seine eigene Ausführungsumgebung ('execution environment'). Dieses enthält Angaben über offene Dateien, das aktuelle Verzeichnis, den aktuellen Modus für die Dateierzeugung, aktuelle Fangpunkte ('traps'), Shell Funktionen, Shell Parameter, aktivierte Optionen, Aliase sowie Prozess IDs.

Startet eine Shell eine neue Sub-Shell, dann wird die Umgebung der aufrufenden Shell nur teilweise übernommen (siehe Schaubild). Insbesondere gilt, dass Veränderungen in der Sub-Shell nicht zurückwirken auf die Umgebung der aufrufenden Shell! Beispiel: Wenn man in der aufrufenden shell eine Variable V auf den Wert "wahr" setzt und die gleiche Variable in einer sub-Shell test1 auf den Wert "falsch", dann hat die Variable V im Bereich der Sub-Shell solange den Wert falsch, solange er nicht in der Sub-Shell geändert wird. Sobald die sub-Shell beendet wird und man die Variable 'V' wieder in der aufrufenden Shell benutzt, hat sie wieder den Wert "wahr". Man kann diese 'Abschottung' der Ausführungsumgebung dadurch aufheben, dass man eine Sub-Shell mit dem Befehl source sub-shell aufruft; dann übernimmt die Ausführungsumgebung der aufrufen Shell die Werte der Sub-Shell.



START





Das UNIX-Dateisystem



Die Dateien sind unter UNIX (linux) hierarchisch geordnet. Die Wurzel ('root') wird durch das Wurzelverzeichnis ('root-directory') / gebildet. Verzeichnisse ('directories') können Dateien aufnehmen. Bei den Dateien unterscheidet man einfache und spezielle Dateien. Letztere sind z.B. Gerätetreiber ('device drivers').

Will man wissen, in welchem aktuellen Verzeichnis ('working directory') man sich gerade befindet, dann kann man sich dies mit dem Befehl pwd explizit anzeigen lassen. Der aktuelle Pfad wird aber auch beim Eingabeprompt der Shell angezeigt.

Mit dem Befehl cd VERZNAME kann man in ein Verzeichnis wechseln, das im aktuellen Pfad erreichbar ist. Befindet man sich z.B. im Root-Verzeichnis, dann könnte man im Beispiel (siehe Schaubild) mit cd home in das home-Verzeichnis wechseln. Will man im Verzeichnisbaum eine Stufe höher gehen, dann gibt man cd .. ein. Die beiden Punkte '..' stehen für das Verzeichnis der nächsthöheren Stufe. Befände man sich gerade in /home/gerd, dann würde der Befehl cd .. in das Verzeichnis /home wechseln. Für grössere Sprünge im Verzeichnisbaum kann man komplette Pfade angeben. Befindet man sich gerade (siehe Schaubild) im Pfad /usr/X11R6/bin und man möchte zum Pfad /home/gerd, dann gibt man das Ziel direkt ein: cd /home/gerd.

Zum Anzeigen des Inhalts eines Verzeichnisses steht der Befehl ls mit diversen Optionen zur Verfügung. Befände man sich im Verzeichnis /home/gerd/bin, dann würde der Befehl ls die Ausgabe liefern: lm test1.

Gibt es in einem Verzeichnis eine Datei file, die man entfernen ('remove') will, dann kann man dies mit dem Befehl rm file tun. Will man alle Dateien innerhalb eines Verzeichnisses entfernen, so wird dies durch den Befehl rm * erreicht. Die Shell ersetzt das spezielle Zeichen Stern '*' nacheinander durch alle Dateinamen, die im Verzeichnis vorkommen.

Ein neues Verzeichnis anlegen kann man mit dem Befehl mkdir VERZNAME. Ein Verzeichnis entfernen kann man mit dem Befehl rmdir VERZNAME, falls das Verzeichnis leer ist. Andernfalls muss man dieses entweder zunächst mit dem zuvor genannten Befehl rm * tun, oder man erledigt die Aufgabe mit einem einzigen Befehl, nämlich rm -r VERZNAME. Der Befehl rm mit der Option -r (r = rekursiv) löscht nämlich zunächst alle Dateien des Verzeichnisses VERZNAME und dann auch noch das Verzeichnis selbst.

Will man sich den Inhalt einer Datei file anzeigen lassen, dann kann man dies mit den Befehlen cat file tun oder mit dem Befehl more file. more hat die Eigenschaft, dass der Dateiinhalt immer nur in slchen Portionen angezeigt wird, wie sie gerade in den aktuellen Fensterausschnit am Bildschirm passen.

Mit dem Befehl cp file_1 ... file_n VERZNAME kann man die Dateien file_1 bis file_n in das Verzeichnis VERZNAME kopieren. Mit cp -R VERZNAME_1 VERZNAME_2 wird das Verzeichnis VERZNAME_1 samt Inhalt in das Verzeichnis VERZNAME_2 kopiert. Einfache Kopien erzeugt man durch cp file_1 file_2. file_2 ist dann eine Kopie von file_1.



START





Ein- und Ausgabeumleitungen; Pipes



Im Rahmen von UNIX (linux) bekommt die Shell ihre Eingaben normalerweise über die Standardeingabe ('stdin'), das ist die Tastatur, und gibt ihre Ergebnisse aus auf die Standardausgabe ('stdio'), das ist das Textfenster, das zur aktiven Shell gehört. Zusätzliche gibt es noch die Standardfehlerausgabe ('stderr'), die ebenfalls auf das Textfenster der aktiven Shell ausgibt.

Diese Voreinstellungen kann man umgehen. Will man die Ausgabe eines Befehles in eine bestimmte Datei mit Namen File leiten, dann gibt man an Befehl > File. In diesem Fall wird die Datei File entweder neu erzeugt oder, wenn sie schon besteht, wird ihr bisheriger Inhalt vollständig überschrieben. Mit dem Befehl Befehl >> File kann man die Ausgabe des Befehls Befehl an den schon vorhandenen Inhalt der Datei File anhängen.

Mit dem Befehl Befehl >File wird zwar die Standardausgabe umgeleitet, nicht aber die Standardfehlerausgabe! Will man diese in eine Datei error umleiten, dann muss man dies explizit wie folgt tun: Befehl 2> error. Will man die Standardfehlerausgabe und zugleich die normale Standardausgabe umleiten, dann muss man schreiben: Befehl 2> error >File. In diesem Fall werden die Fehlermeldungen alle nach error geleitet und die normale Ausgabe nach File1

Analog kann man auch die Eingabe umleiten. Mit Befehl < File liest der Befehl aus der Datei File und gibt das Ergebnis auf die Standardausgabe. Im Fall von Shell-Programmen kann man sogar beide Vorgehensweisen 'mischen'. Eine Sequenz Befehl CmdLineArg < File ist möglich, wenn der Shell-Befehl sowohl die Positionsparameter abfragt (siehe unten) als auch den Befehl read benutzt. Der Befehl read liest die Standardeingabe bzw. den Inhalt der Datei File und der Positionsparameter $1 übernimmt das Kommandozeilenargument CmdLineArg (siehe Beispiel für solch ein Shell-programm im Schaubild).

Neben der Umleitungen von Ein- und Ausgaben bietet die Shell auch die praktische Möglichkeit, Befehls-Pipelines zu bilden, oder kurz Pipes. Eine Pipe entsteht dadurch, dass man die Ausgabe eines Befehls gleich wieder als Eingabe für einen anderen Befehl benutzt. Eine einfache Pipe ohne zusätzliche Ein- und Ausgabeumleitungen wäre die folgende: Befehl_1 | ... | Befehl_n. Zum Beispiel die Befehlssequenz ls |sort: ls listet alle Dateien des aktiven Verzeichnisses auf und sort sortiert diese Namen alphabetisch.



START





Editoren



(i) Mini vi



Um Shell-Programme erstellen zu können -dies sind längere Befehlssequenzen, die in einer Datei als Text gespeichert sind- benötigt man einen Editor. Ein Editor erlaubt grundsätzlich die Erstellung von Texten. Spezielle Editoren erlauben die Erstellung und Bearbeitung von Grafiken. Heute gibt es Programme, in denen unterschiedliche Editoren zu einem Paket integriert sind (z.B. StarOffice, Applixware). Unter UNIX (linux) werden zahlreiche Editoren angeboten, sowohl für Texte wie auch für Grafiken. Wir werden hier nur zwei Editoren kurz behandeln: den vi, der zum UNIX-Urgestein gehört, sowie xemacs.

Für kleine Operationen mit Dateien ist der vi effizient. Für grossere Projekte ist er zu umständlich. Für diese werden wir den xemacs benutzen.

Der tatsächliche Befehlsumfang des vi ist sehr mächtig. Wir werden hier nur eine Untermenge der Befehle benutzen. Diesen beschränkten vi nennen wir Mini vi. Zur schnellen Orientierung sei auf das Schaubild verwiesen.

Grundsätzlich kann man beim vi zwischen dem Befehlsmods und dem Aktionsmodus unterscheiden. Befindet sich der vi im Befehlsmodus, dann kann er durch Eingabe eines Aktionspatterns dazu gebracht werden, einen bestimmten Aktion innerhalb des Aktionsmodus einzunehmen. Die Eingabe von a führt z.B. dazu, dass er in den Aktionsmodus Anfügen ('append') übergeht. In diesem Modus fügt er Zeichen am Zeilenende an. Bei der Eingabe von i geht er in den Aktionsmodus Einfügen ('insert') über. Er fügt dann an der Position, an der sich der Kursor gerade befindet, Zeichen ein. Mit yy wird die aktuelle Zeile in einen Puffer kopiert, der durch die Eingabe p ('print', drucken) in einer Zeile auf den Bildschirm gedruckt werden kann. Durch x wird das Löschen einzelner Zeichen eingeleitet; mit dd löscht man die ganze aktuelle Zeile. Mittels der Pfeiltasten für hoch, runter, rechts, links kann man den Kursor von der aktuellen Position aus bewegen. Mit <Shift+$> kann man zum Zeilenende springen.

Um einen Aktionsmodus wieder zu verlassen muss man die Taste Escape (ESC) drücken.

Der vi wird gestartet, indem man auf der Shell-Befehlszeile vi file eingibt. Existiert die Datei file schon, dann wird sie dadurch geöffnet. Andernfalls kann man auf diese Weise eine neue Datei file erstellen.

Man beendet eine Sitzung mit dem vi dadurch, dass man die Tasten <ESC><Shift+q> betätigt und dann entweder die Taste <q> drückt für Beenden ohne speichern ('quit') oder aber die beiden Tasten <w>+<q> für Speichern und Beenden ('write - quit').


(ii) Mini xemacs



Für grössere Operationen mit Dateien bietet der xemacs mehr Komfort als der vi. Da der tatsächliche Befehlsumfang des xemacs sehr mächtig ist, seien hier nur die wichtigsten Befehle kurz vorgestellt (siehe auch das Schaubild).

Man kann beim xemacs grob zwischen zwei Zuständen unterscheiden: dem Buffer Modus und dem File Modus, wobei der File Modus letztlich auch ein Buffer Modus ist, allerdings eben ein Buffer, der Bezug zu einer bestimmten Datei hat.

Startet man xemacs ohne zusätzlichen Argumente durch Eingabe von xemacs <RET>( := Return Taste) auf der Befehlszeile der Shell, dann erscheint ein Begrüssungsbildschirm. Ein weiteres<RET> öffnet dann einen Buffer zum Editieren eines Textes. Dieser Text hat keinen Bezug zu einer Datei. Man kann diesen reinen Buffer Modus dann wieder verlassen duch die Tasten C-x C-c. 'C' steht für die Taste Control bzw. Ctrl. Will man den Text im Buffer mit C-x C-s speichern oder lädt man den Text einer Datei mittels C-x C-f in den Buffer, dann wechselt man in den File Modus.

Vom Start gelangt man direkt in den File Modus, indem man auf der Befehlszeile der Shell den Befehl xemacs file eingibt. Es wird dann direkt ein Buffer geöffnet mit dem Text der Datei file. Diesen kann man dann editieren. Mit C-x C-s wird der Text unter gleichem Namen zurückgespeichert. Mit C-x C-w kann man einen neuen Namen vergeben. Gleichfalls kann man weitere Dateien mit C-x C-f öffnen. Man kann den File Modus dann wieder verlassen duch die Tasten C-x C-c.



START


Shell-Programme

Ein Shell-Programm (oft auch Shell-Script genannt) ist eine Textdatei, die optional eine erste Zeile der Art '#! /bin/bash' enthält sowie eine Liste von Shell-Befehlen (siehe Schaubild).

Die optionale erste Zeile '#! /bin/bash' gibt an, welches Programm für die Abarbeitung dieser Datei zuständig ist. In diesem Fall ist es das Programm 'bash' mit dem Pfad '/bin'. Lässt man diese Zeile weg, dann benutzt das Betriebssystem seine Standardeinstellung. Gibt man die Zeile in einem Script mit Namen file an, aber mit einer fehlerhaften Angabe, dann liefert das System bei Aufruf des Scripts auf der Befehlszeile eine Fehlermeldung zurück:

>file.sh

>bash: ./file.sh: bad interpreter: Datei oder Verzeichnis nicht gefunden

Mit dem Befehl:

>bash file.sh

kann man das Script 'file.sh' dann aber dennoch starten.

Würde man statt '#! /bin/bash' eingeben '#! /bin/cat', dann würde bei Aufruf der Datei file.sh das Programm cat aktiviert, das den Inhalt der Datei file.sh ausdrucken würde.



SHELLSCIPT0



Ein einfacher Shell-Befehl ('simple command') besteht aus einer Folge von Worten, getrennt durch Leertasten, die durch einen Kontroll-Operator ('control operator') abgeschlossen werden (siehe Schaubild).

Das erste Wort deines einfachen Befehls muss ein Befehlswort ('command word') sein.

Als solche Befehlsworte stehen entweder die eingebauten ('built-in') befehlsworte zur Verfügung oder solche, die ein Benutzer definiert ('user defined') hat.

Beispiele für eingebaute Befehlsworte sind: {cd, pwd, cp, rm, ls, ...}.

Ein Benutzer kann eigene Befehlsworte definieren, indem er ein Shellscript file.sh schreibt, es als file.sh abspeichert und mittels chmod u+x file.sh dieser Datei Ausführungsrechte mittels des Befehls chmod zuweist. Den Befehl chmod u+x file.sh könnte man auch lesen: Ändere den Modus ('change mode' = 'chmod') der Datei file.h dahingehend, dass der Besitzer 'user' (= u) auf diese Datei auch Ausführungsrechte ('execute' = x) besitzt. Damit kann der Befehlsvorrat für die Shell beliebig erweitert werden.

Das Ende der Wortkette wird durch die Kontrolloperatoren gebildet. Dabei ist der einfachste Kontrolloperator das Zeichen '\n' ('newline', 'Zeilenvorschub'). Dies wird beim Editieren des Shellscripts dadurch generiert, dass man am Zeilenende die <RET>-Taste drückt.


SHELLSCIPT0b



Ein einfaches Shellscript mit dem Namen hallo.sh ist nebenstehend abgebildet. In der ersten Zeile wird gesagt, dass dieses Skript durch das Programm bash mit dem Pfad /bin abgearbeitet werden soll. Geschützt durch ein Kommentarzeichen '#' wird der Name des Programms mit hallo.sh angegeben. Dann folgenden drei einfache Befehle. Jeder dieser Befehle ist durch den Kontrolloperator '\n' abgeschlossen. Das Befehlswort ist in jedem Fall das Wort echo. Die nachfolgenden Worte bilden jeweils Argumente für das Befehlswort echo. Bei dem ersten Befehl besteht das Argument aus dem Wort "Hallo", beim zweiten Befehl aus dem Wort "Arg0" konkateniert mit dem Inhalt des Shellparameters $0; entsprechend beim dritten Befehl.

Die Konkatenierung von zwei Worten W1 und W2 bedeutet, dass zwei Wort zu einem Wort zusammengefügt werden. Im Falle von "Arg0 ="$0 ist dies nicht auf den ersten Blick sichtbar. Es gilt hier: W1 := "Arg0 =" und W2 := Der Inhalt des Shellparameters $0. Variablen und deren Inhalte werden weiter unten noch behandelt.


START




SHELLSCIPT1



Shell-Parameter/ -Variablen

Bezüglich der Verwendung der Terme Paramter bzw. Variable gibt es einen nicht ganz strikten Gebrauch im Kontext der Shell-Literatur. Deswegen hier kurze Bemerkungen zur Verwendung dieser Terme.

In der Theorie der formalen Sprachen ist eine Variable normalerweise eine Zeichenkette, die im Rahmen von Substitutionsregeln durch bestimmte Werte eines Wertevorrates ersetzt ('substituiert') werden darf.

Entsprechend wird der Begriff der Variablen auch in Programmiersprachen benutzt. In einer Variablen-Deklaration der Sprache C wird eine bestimmte Zeichenkette (der 'identifier', 'Variablenname') als Variable eingeführt und dieser ein Typ zugewiesen. Zusätzlich zu einer rein formalen Sprache (wie z.B. der Prädikatenlogik 1.Stufe) korrespondiert den Variablen einer Programmiersprache aber noch Speicherplatz ('storage allocation'). Dieser Speicherplatz ist zusätzlich charakterisierbar durch den Umfang und die zeitliche Dauer ('duration', 'extend'), weswegen Variablen von Programmiersprachen zusätzlich zum Datentyp auch noch hinsichtlich der Art und Weise der Speicherung ('Storage Classe Specifiers') definiert werden können (und müssen).

Im Referenzpapier zur Bash-Shell wird nun statt von Variablen in manchen Kontexten auch von Parametern gesprochen. Wortwörtlich heisst es dort zu Parameter:

"A parameter is an entity that stores values. It can be a name, a number, or one of the special characters listed below ( gemeint sind: {* , "$*", @ , # , ? , - , $ , ! , 0 , _ }).

Diese Aussage ist schwer zu verstehen, denn einmal (i) wird gesagt, dass ein Parameter etwas sei, das Werte speichert; dies ist nach normalem Verständnis ein Speicherplatz. Zugleich (ii) wird aber auch von Zeichen ('characters') gesprochen, die Parameter repräsentieren. Und in der Tat sind Positional Parameters sowie Special Parameters bestimmte Zeichenketten, durch die Parameter repräsentiert werden.

Im Text heisst es dann aber weiter: "For the shell's purposes, a variable is a parameter denoted by a name". Und "A parameter is set if it has been assigned a value. .... Once a variable is set, it may be unset only by using the unset builtin command. A variable may be assigned to by a statement of the form name=[value]...". Im weiteren Verlauf taucht der Term Variable dann garnicht mehr auf; es ist immer nur die Rede von Parametern.

Von der Intention her 'scheint' es also so zu sein, dass mit dem Term Parameter ein Speicherplatz gemeint ist, der bestimmte Werte ('values') beinhalten kann, und dass die Kombination Name+Parameter eine Variable ergibt. Der tatsächliche Gebrauch des Terms ist nicht so klar. Man muss sich also entscheiden. Während ein Autor wie Rainer KRIENKE in seinem sehr lesenwerten Buch konsequent den Term Variable benutzt, wo das Bash-Shell Referenzpapier von Parametern spricht, wollen wir hier der Terminologie des Bash-Shell Referenzpapiers folgen und im Folgenden immer den Term Parameter benutzen.



Die grobe Unterteilung bei den Parametern kann man dem nebenstehenden Schaubild entnehmen.

Die (normalen) Parameter, das sind Zeichenketten ('Namen', 'identifier'), denen man durch das Gleichheitszeichen '=' einen Wert zuordnen kann. Gibt man keinen Wert an, wird automatisch eine leere Zeichenkette ('null string') als Wert angenommen. Mittels des Befehles unset kann man solche Wertzuweisungen wieder rückgängig machen.



SHELLPARAMS



Positions Parameter werden von der Shell automatisch generiert, wenn einem Shellbefehl beim Aufruf Argumente übergeben werden. Die Zählung der übergebenen Argumente beginnt bei '1'. '1' ist also der Name des ersten Positionsparameters. Ab der Position '10' müssen die Ziffern, die Positionsparamter bezeichnen, mit geschweiften Klammern zusammengefasst werden, also '{11}' für das 11.Argument. Positionsparatern kann man zusätzlich nur durch set einen Wert zuweisen, z.B. set arg1 arg2 weisst dem Positionsparameter {1} den Wert 'arg1' zu und dem Positionsparameter {2} den Wert 'arg2'.

Das Beispielskript parameter-demo.sh zusammen mit einer Wiedergabe der Shellausgabe daneben zeigt das Folgende: Zunächst wird mit echo zur Kontrolle der Wert der Positionsparameter 1 und 2 ausgegeben. Dann zum Vergleich der Wert der Spezial-Parameter '*' sowie '@'. Schliesslich werden die Werte der Positionsparameter 1 und 2 durch den Befehl set auf '111' bzw. '222' gesetzt. Ausserdem wird dem (gewöhnlichen) Parameter ''var' der Wert '333' zugewiesen. Mittels echo werden dann die Werte dieser Parameter erneut ausgegeben. Man sieht, dass die Werte der Positionsparameter verändert wurden; ebenfalls hat var den Wert '333' angenommen. Dann wird noch die Wirkung von unset bzgl. var demonstriert.




START



SHELLPARAMS-DEMO-SKRIPT

Die speziellen Parameter können nur gelesen werden. Ihre Werte werden ausschliesslich intern von der Shell selbst gesetzt.

'*' Expandiert zu den Poitionsparametern, beginnend bei 1.

"$*" ist gleichbedeutend mit "$1c$2c...", wobei c dem ersten Zeichen der IFS-Variablen entspricht.

'@' Expandiert zu den Poitionsparametern, beginnend bei 1.

"$@" ist gleichbedeutend mit "$1" "$2" . Wenn es keine Positionsparameter gibt, wird nichts expandiert.

'#' Expandiert zur Anzahl der Positionsparameter (Dezimal)

'?' Expandiert zum Exit-Status der zuletzt im Vordergrund ausgeführten Pipeline

'-' Expandiert zu den aktuellen Options-Flags, entweder gesetzt durch den 'set'-Befehl oder durch die Shell selbst.

'$' Expandiert zur Prozess-ID der Shell. In einer Subshell zur Prozess-ID der aufrufenden Shell.

'!' Expandiert zur Prozess-ID des zuletzt im Hintergrund ausgeführten Befehls.

'0' Expandiert zum Namen des Shellskripts. Wenn die Shell mit der '-c'-Option gestartet wird, dann ist $0 auf das erste

Argument hinter der Zeichenkette gesetzt, die ausgeführt werden soll, falls vorhanden.

'_' Beim Starten der Shell gesetzt auf den absoluten Dateinamen des auszuführenden Skripts entsprechend der Argumentliste. Anschliessend expandiert '_' zum letzten Argument des letzten Befehls; ebenfalls expandiert zum vollen Pfadnamen jedes ausgeführten Befehls. Beim Prüfen der Mail hält dieser Parameter den Namen der Mail-Files.



SHELLPARAMS-DEMO-EXECUTION1



Shell-Funktionen

Shell-Funktionen bilden ein Mittel, um Shell-Befehle so zu gruppieren, dass man sie wie einen einzigen Shell-Befehl nutzen kann. Dieser Befehl wird dann in der aktuellen Shellumgebung ausgeführt. Es wird kein eigener neuer Prozess erzeugt.

Die Syntax des Befehls (siehe Schaubild) ist einfach. Das Keyword function (das optional ist) gefolgt von einem Namen, einem runden Klammerpaar und einer Liste von Befehlen in geschweiften Klammern mit einem abschliessenden Semikolon. Wird das Keyword function benutzt, dann sind die runden Klammern überflüssig.

Wird die Funktion mit ihrem Namen aufgerufen, dann werden die Befehle aus der Befehlsliste ausgeführt. Der Exit-Status der Funktion ist entweder der Exit-Status des zuletzt ausgeführten Befehls oder jener Wert, der dem Befehl return als letztes übergeben wird.

Wenn beim Aufruf der Funktion Argumente übergeben werden, dann werde diese den Positionsparametern der Fuktion übergeben. Der spezielle Parameter '#' wird zur Anzahl der Argumente expandiert.

Variablen innerhalb einer Funktion können mit dem Keyword local als lokale Variablen deklariert werden.

Ferner sind Funktionen rekursiv. Es gibt keine Begrenzung bzgl. der Anzahl der wiederholten Aufrufe.




SHELLSKRIPT-FUNKTION1

SHELLSKRIPT-SYNTAX1

Das nebenstehende Shell-Skript demonstriert auf einfache Weise, wie die Definition einer neuen Funktion mit Namen antwort im Text vor dem eigentlichen Shellskript stehen muss, damit die Funktion antwort im nachfolgenden Aufruf auch als neue Funktion erkannt wird.

Da es normalerweise wichtig ist, über die Rückgabewerte von Funktionen informiert zu sein, muss es eine Möglichkeit geben, diese auszuwerten. Eine Variante wird im Skript demonstriert. Man fragt den speziellen Parameter $? unmittelbar nach der Ausführung der Funktion antwort ab, da dieser Parameter immer den Exitstatus der zuletzt ausgeführten Funktion repräsentiert.


START

SHELLSKRIPT-FUNKTION2



Die Arbeitsweise der Shell

Nach dem Bash-Referenz Dokument finden nach der Aktivierung eines Shell-Befehls die folgenden Verarbeitungsschritte statt:


(1) Aufspaltung der Befehlszeile in Tokens

(2) Klammer -Expansion ('brace-expansion')

(3) Tilde-Expansion

(4) Parameter-Expansion

(5) Befehls-Substitution

(6) Arithmetische Substitution

(7) Aufspaltung von Worten

(8) Dateinamen-Expansion

Falls verfügbar, parallel zu (4) - (7) Prozesse-Substitution

(9) Beseitigung von Anführungszeichen



Für Details dieser Prozesse sei wieder auf das Bash-Referenz Dokument verwiesen. Hier nur einige erste Informationen.

(2) Klammer -Expansion ('brace-expansion')

Dieser Expansion ist rein textuell.

Bsp.:

bash$ echo a{d,c,b}e

ade ace abe

Anwendungen:

mkdir /usr/local/src/bash/{old,new,dist,bugs}

chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}



(3) Tilde-Expansion

'~' als Wert von $HOME führt zu folgenden Expansionen:

~/foo `$HOME/foo'

'~+' als Wert von $PWD

'~-' als Wert von $OLDPWD

'~N' mit N als eine Zahl verweist auf das N-te element im Verzeichnis-Stack `dirs +N'

'~+N' entpricht `dirs +N'

'~-N' entspricht `dirs -N'



(4) Parameter-Expansion

${parameter} ist die Grundform

${parameter:-word} Wenn der Parameter keinen Wert hat, dann wird dafür der Wert von Wort substituiert ('substituted').

${parameter:=word} Wenn der Parameter keinen Wert hat, dann wird ihm der Wert von Wort zugeordnet ('assigned').

${parameter:?word} Wenn der Parameter keinen Wert hat, dann wird die Expansion des Wortes nach Standard-Error und zur Shell geschrieben. Falls nicht interaktiv, dann Exit.

${parameter:+word} Wenn der Parameter keinen Wert hat, dann wird nichts substituiert.

${parameter:offset}

${parameter:offset:length} Beginnend beim Zeichen gekennzeichnet durch 'offset' wird der Parameter entweder um die Anzahl 'length' exandiert oder, falls 'length' fehlt, bis zum Ende.

${#parameter} Liefert die Länge der Zeichenkette, die als Wert im Parameter gespeichert ist.ubstituted is the number of elements in the array.

${parameter#word}

${parameter##word} Es wird das in 'word' gegebene Muster vom Wert des Parameters von links her entfernt. Bei '#' wird das kleinste passende, bei '##' das grösste passende Muster entfernt.

${parameter%word}

${parameter%%word} Analog zu '#' bzw. '##', nur von rechts nach links.

${parameter/[#,%]pattern/string} Das erste Vorkommnis von pattern im Wert von Parameter wird durch string ersetzt.

${parameter//[#,%]pattern/string} Alle solche Vorkommnisse werden ersetzt. Bei '#pattern' muss das Muster vom Anfang her matchen, bei '%pattern' vom Ende her.




(5) Befehls-Substitution

$(Befehl) oder `Befehl`

Der Name des Befehls wird durch das Ergebnis der Befehlsausführung ersetzt. Bei Benutzung der Form $(Befehl) wird kein Zeichen zwischen den Klammern speziell behandelt.



(6) Arithmetische Substitution

$(( expression ))

Alle Token innerhalb der Klammern durchlaufen Parameter-Expansion, Befehls-Substitution sowie Anführungszeichen-Entfernung.




(7) Aufspaltung von Worten

Die Shell analysiert die Ergebnisse der Parameter-Expansion, Befehls-Substitution und der arithmetischen Expansion, die nicht innerhalb doppelter Anführungszeichen vorkamen. Die Shell behandelt dabei jedes Zeichen von $IFS als ein Begrenzer, und spaltet das Ergebnis der anderen Expansionen in Worte zwischen den Begrenzern. Wenn der Wert von IFS Null ist, findet keine Aufspaltung von Worten statt. Explizite Null-Argumente ("" bzw. '') bleiben erhalten. Aufspaltung in Worte findet nicht statt, wenn es zu keiner Expansion kam.




(8) Dateinamen-Expansion

Sofern nicht mit 'set -f' Dateinamen-Expansion ausgeschaltet wurde, sucht die shell die Zeichenketten ab nach den Zeichen `*', `?', `(', and `['. Wird eines dieser Zeichen gefunden, dann wird das ganze Wort als ein Muster angesehen und durch eine alphabetisch sortierte Liste von Dateinamen, die dieses Muster matchen. Werden keine passenden Dateinamen gefunden und die Option ' nullglob' ist nicht aktiv, dann wird nichts getan. Ist ' nullglob' aktiv, dann wird das Wort entfernt. Mit der Option ' nocaseglob' kann man die Berücksichtigung von Gross-/Kleinschreibung ausschalten. Weitere wichtige Zeichen oder optionen sind '.', 'dotglob', 'nocaseglob', 'nullglob' , ' GLOBIGNORE'.



Falls verfügbar, parallel zu (4) - (7) Prozesse-Substitution

(list) oder >(list) oder <(list)

Prozess-Substitution wird unterstützt von Systemen, die named pipes (FIFOs) oder die /dev/fd-Methode zur Benennung von offenen Dateien zur Verfügung stellen. Die Liste der Prozesse wird abgearbeitet und ihr Input wie auch ihr Output sind entweder an ein FIFO oder an eine Datei in '/dev/fd' gebunden. Der Name dieser Datei wird als Argument zum aktuellen Befehl als Ergebnis der Expansion durchgereicht.

Wenn die form >(list) benutzt wird, dann liefert das Schreiben in die Datei Input für die Liste. Bei der Form <(list) muss der Dateiname, der durchgereicht wird, gelesen werden, um den Output der Liste zu bekommen.




(9) Beseitigung von Anführungszeichen

Nach Ausführung aller zuvor erwähnten Expansionen werden alle nicht durch Anführungszeichen gekennzeichnete Vorkommnisse der Zeichen `\', `'' und `"', die nicht das Ergebnis einer der zuvor ausgeführten Expansionen sind, entfernt.


START