In der Shell stehen viele Mechanismen zur Verf<72>gung, die auch aus anderen Sprachen bekannt sind. Um den Umfang dieses Dokuments nicht zu sprengen, werden an dieser Stelle nur die wichtigsten vorgestellt.
Die folgenden Mechanismen stehen in der Standard-Shell bereit, um mit Variablen zu hantieren. Bei allen Angaben ist der Doppelpunkt optional. Wenn er aber angegeben wird, mu<6D> die \textsl{Variable} einen Wert enthalten. \nopagebreak
Bei der Angabe von Dateinamen k<>nnen eine Reihe von Meta-Zeichen\footnote{Meta-Zeichen werden auch Wildcards, Joker-Zeichen oder Platzhalter genannt.} verwendet werden, um mehrere Dateien gleichzeitig anzusprechen oder um nicht den vollen Dateinamen ausschreiben zu m<>ssen.
Bei einigen Tools (\texttt{ex}, \texttt{sed} und \texttt{ed}) werden zwei Muster angegeben: Ein Suchmuster (links) und ein Ersatzmuster (rechts). Nur die folgenden Zeichen sind in einem Ersatzmuster g<>ltig:\nopagebreak
\medskip\emph{Beispiele:} egrep- oder awk-Muster\nopagebreak
\LTXtable{\textwidth}{tab_beisp_muster_egrep.tex}
\medskip\emph{Beispiele:} ex- oder vi-Muster\nopagebreak
\LTXtable{\textwidth}{tab_beisp_muster_ex.tex}
\medskip\emph{Beispiele:} sed- oder grep-Muster\nopagebreak
\LTXtable{\textwidth}{tab_beisp_muster_sed.tex}
\medskip\emph{Beispiele:} Suchen und Ersetzen mit \texttt{sed} und \texttt{ex}. Im Folgenden werden Leerzeichen durch \Ovalbox{SPACE} und Tabulatoren durch \Ovalbox{TAB} gekennzeichnet. Befehle f<>r ex werden mit einem Doppelpunkt eingeleitet.\nopagebreak
\section{Eltern und Kinder: Proze<7A>ordnung\label{prozessordnung}\index{Prozess|(textbf}\index{PID|see{Prozess}}\index{Parent-Prozess|see{Prozess}}\index{PPID|see{Prozess}}\index{Subshell|(textbf}}
Jedes laufende Programm auf einem Unixoiden System besteht aus einem oder
mehreren Prozessen, die jeweils eine eigene Proze<7A>-ID (PID) haben. Erzeugt ein
Programm mehrere Prozesse, sind die zu einer Proze<7A>gruppe zusammengefa<66>t. Jeder
laufende Proze<7A>\footnote{Es gibt eine Ausnahme: der Init-Proze<7A>, der immer die
PID 1 hat, hat keine Eltern. Er stammt direkt vom Kernel ab.} verf<72>gt <20>ber
genau eine Parent-Proze<7A>-ID (PPID). Das ist die ID des Prozesses, der den
jeweiligen Proze<7A> erzeugt hat. Man spricht in diesem Zusammenhang tats<74>chlich
von Eltern- bzw. Kind-Prozessen.
Diese Zusammenh<6E>nge lassen sich sehr sch<63>n durch die Ausgabe des Kommandos
\texttt{pstree} oder \texttt{ps -efH} darstellen, letzteres zeigt auch gleich
die PIDs und die PPIDs an.
Wenn in einer Shell ein Kommando gestartet wird, ist es ein Kind dieser Shell.
Wird ein Skript gestartet, <20>ffnet es sich seine eigene Shell (siehe
\ref{auswahl_der_shell}) und f<>hrt sich innerhalb dieser aus. Die Shell des
Skriptes ist dabei ein Kind der interaktiven Shell, die einzelnen Kommandos des
Skriptes sind Kinder der Skript-Shell.
Eine solche Shell-in-Shell-Umgebung wird `Subshell' genannt, dieser
Mechanismus~--~und somit auch der Begriff~--~tauchen immer wieder auf.
Wichtig in diesem Zusammenhang ist das Verst<73>ndnis f<>r die Vererbung zwischen
den beteiligten Prozessen. Wenn in einer Shell eine Variable definiert und
exportiert wird, existiert diese auch f<>r die Kind-Prozesse. Gleiches gilt
beispielsweise f<>r einen Verzeichnis-Wechsel. Umgekehrt gilt dies nicht: ein
Proze<EFBFBD> kann die Umgebung des Parent-Prozesses nicht ver<65>ndern. Das geschieht
nicht zuletzt aus Sicherheitsgr<67>nden so.
Will man die <20>nderungen eines Skriptes <20>bernehmen~--~beispielsweise wenn ein
Skript die Benutzerumgebung konfigurieren soll (.bashrc, .profile und
Konsorten)~--~mu<6D> das explizit angefordert werden. Dazu ruft man es mit einem
vorangestellten \texttt{source}\index{source=\texttt{source}} bzw. in der
Kurzform mit einem vorangestellten Punkt auf. Weiteres zu dem Thema findet sich
im Abschnitt \ref{source}.
Besonders mu<6D> man diesen Umstand im Hinterkopf behalten, wenn mit
Pipelines\index{Pipe} (siehe Abschnitt \ref{befehlsformen}) gearbeitet wird.
Dabei werden auch Kommandos in Subshells ausgef<65>hrt, was dann dazu f<>hrt da<64>
Variablen belegt werden die dann nach Ausf<73>hrung der Pipeline pl<70>tzlich wieder
leer sind. Die Abschnitte \ref{subshellschleifen} und \ref{daten_hochreichen}
widmen sich diesem mitunter recht <20>rgerlichen Thema.
Bei der Shell-Programmierung verf<72>gt man <20>ber <20>hnliche Konstrukte wie bei anderen Programmiersprachen, um den Ablauf des Programms zu steuern. Dazu geh<65>ren Funktionsaufrufe, Schleifen, Fallunterscheidungen und dergleichen.\nopagebreak
Dieser Befehl tut nichts, au<61>er den Status 0 zur<75>ckzugeben. Er wird benutzt, um Endlosschleifen\index{Endlosschleife} zu schreiben (siehe unter \ref{while}), oder um leere Bl<42>cke in \texttt{if}- oder \texttt{case}-Konstrukten\index{if=\texttt{if}}\index{case=\texttt{case}} m<>glich zu machen.
\medskip\emph{Beispiel:} Pr<50>fen, ob jemand angemeldet ist:\nopagebreak
Ein Shell-Skript kann in keiner Weise Einflu<6C> auf die umgebende Shell nehmen. Das hei<65>t, da<64> es beispielsweise nicht m<>glich ist, in einem Skript Variablen zu setzen, die dann in der aufrufenden Shell zur Verf<72>gung stehen. Genauso wenig ist es m<>glich, da<64> ein Skript den Pfad <20>ndert, in dem man sich befindet. Der Grund f<>r dieses Verhalten ist die Systemsicherheit. Man will verhindern, da<64> ein Skript unbemerkt <20>nderungen an der Benutzerumgebung vornimmt.
Wenn es aber doch gew<65>nscht wird, da<64> ein Skript die Umgebung des Benutzers <20>ndern kann, dann mu<6D> es mit dem Source-Kommando aufgerufen werden. Das wird in der Form \verb\source skriptname\ bzw. \verb\. skriptname\ angegeben. Er bewirkt <20>hnliches wie ein \verb\#include\ in der Programmiersprache C.
Die `gesourcte' Datei wird eingelesen und ausgef<65>hrt, als ob ihr Inhalt an der Stelle des Befehls stehen w<>rde. Diese Methode wird zum Beispiel beim Login in den Konfigurationsdateien des Benutzers (z. B. \verb\.profile\,\verb\.bashrc\) oder w<>hrend des Bootvorgangs in den Init-Skripten benutzt, um immer wieder ben<65>tigte Funktionen (Starten eines Dienstes, Statusmeldungen auf dem Bildschirm etc.) in einer zentralen Datei pflegen zu k<>nnen (siehe Beispiel unter~\ref{init-skript}).
Es ist in der Shell auch m<>glich, <20>hnlich wie in einer `richtigen' Programmiersprache Funktionen zu deklarieren und zu benutzen. Da die Bourne-Shell (\verb\sh\) nicht <20>ber Aliase\index{Aliase} verf<72>gt, k<>nnen einfache Funktionen als Ersatz dienen.
Die \texttt{if}-Anweisung in der Shell-Programmierung macht das gleiche wie in allen anderen Programmiersprachen, sie testet eine Bedingung auf Wahrheit und macht davon den weiteren Ablauf des Programms abh<62>ngig.
Die Syntax der \texttt{if}-Anweisung lautet wie folgt:\nopagebreak
\LTXtable{\textwidth}{tab_if.tex}
Wenn die \textsl{Bedingung1} erf<72>llt ist, werden die \textsl{Befehle1} ausgef<65>hrt; andernfalls, wenn die \textsl{Bedingung2} erf<72>llt ist, werden die \textsl{Befehle2} ausgef<65>hrt. Trifft keine Bedingung zu, sollen die \textsl{Befehle3} ausgef<65>hrt werden.
Bedingungen werden normalerweise mit dem Befehl \texttt{test}\index{test=\texttt{test}} (siehe unter \ref{bedingungen}) formuliert. Es kann aber auch der R<>ckgabewert\footnote{Siehe unter \ref{exitcode}.}\index{R<EFBFBD>ckgabewert} jedes anderen Kommandos ausgewertet werden. F<>r Bedingungen, die auf jeden Fall zutreffen sollen steht der Null-Befehl (\texttt{:}, siehe unter \ref{null-befehl}) zur Verf<72>gung.
\medskip\emph{Beispiele:} Man achte auf die Positionierung der Semikola\footnote{Und man verzeihe mir einen eventuell falschen Plural\ldots :-)}.\nopagebreak
Auch die \texttt{case}-Anweisung ist vergleichbar in vielen anderen Sprachen vorhanden. Sie dient, <20>hnlich wie die \texttt{if}-Anweisung zur Fallunterscheidung\index{Fallunterscheidung|see{case}}\index{Fallunterscheidung|see{if}}. Allerdings wird hier nicht nur zwischen zwei F<>llen unterschieden (Entweder / Oder), sondern es sind mehrere F<>lle m<>glich. Man kann die \texttt{case}-Anweisung auch durch eine geschachtelte \texttt{if}-Anweisung v<>llig umgehen, allerdings ist sie ein elegantes Mittel um den Code lesbar zu halten.
Die Syntax der \texttt{case}-Anweisung lautet wie folgt:\nopagebreak
\LTXtable{\textwidth}{tab_case.tex}
Wenn der \textsl{Wert} mit dem \textsl{Muster1}<20>bereinstimmt, wird die entsprechende Befehlsfolge\index{Befehls>-folge} (\textsl{Befehle1}) ausgef<65>hrt, bei <20>bereinstimmung mit \textsl{Muster2} werden die Kommandos der zweiten Befehlsfolge\index{Befehls>-folge} (\textsl{Befehle2}) ausgef<65>hrt, usw. Der letzte Befehl in jeder Gruppe mu<6D> mit \texttt{;;} gekennzeichnet werden. Das bedeutet f<>r die Shell soviel wie `springe zum n<>chsten \texttt{esac}', so da<64> die anderen Bedingungen nicht mehr <20>berpr<70>ft werden.
In den Mustern sind die gleichen Meta-Zeichen\index{Meta-Zeichen} erlaubt wie bei der Auswahl von Dateinamen. Das bedeutet, da<64> man durch ein einfaches \texttt{*}\index{*=\texttt{*}} den Default-Pfad kennzeichnen kann. Dieser wird dann durchlaufen, wenn kein anderes Muster zutrifft. Wenn in einer Zeile mehrere Muster angegeben werden sollen, m<>ssen sie durch ein Pipezeichen (\texttt{|}, logisches ODER) getrennt werden.
Die \texttt{while}-Schleife ist wieder ein Konstrukt, das einem aus vielen anderen Sprachen bekannt ist: die Kopfgesteuerte Schleife.
Die Syntax der \texttt{while}-Schleife lautet wie folgt:\nopagebreak
\LTXtable{\textwidth}{tab_while.tex}
Die \textsl{Befehle} werden so lange ausgef<65>hrt, wie die \textsl{Bedingung} erf<72>llt ist. Dabei wird die \textsl{Bedingung} vor der Ausf<73>hrung der \textsl{Befehle}<20>berpr<70>ft. Die \textsl{Bedingung} wird dabei <20>blicherweise, genau wie bei der \texttt{if}-Anweisung, mit mit dem Befehl \texttt{test}\index{test=\texttt{test}} (siehe unter \ref{bedingungen}) formuliert. Wenn die Ausf<73>hrung eines Schleifendurchlaufs bzw der ganzen Schleife abgebrochen werden soll, m<>ssen die Kommandos \texttt{continue}\index{continue=\texttt{continue}} (\ref{continue}) bzw. \texttt{break}\index{break=\texttt{break}} (\ref{break}) benutzt werden.
\medskip\emph{Beispiel:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_while.tex}
Eine Standard-Anwendung der \texttt{while}-Schleife ist der Ersatz f<>r die
\medskip\emph{Beispiel:} Hier wird die Bedingung nicht per \texttt{test} sondern mit dem R<>ckgabewert\index{R<EFBFBD>ckgabewert} des Programms \texttt{grep}\index{grep=\texttt{grep}} formuliert.\nopagebreak
Die Syntax der \texttt{continue}-Anweisung lautet wie folgt:\nopagebreak
\LTXtable{\textwidth}{tab_continue.tex}
Man benutzt \texttt{continue} um die restlichen Befehle in einer Schleife zu <20>berspringen und mit dem n<>chsten Schleifendurchlauf anzufangen. Wenn der Parameter \textsl{n} angegeben wird, werden \textsl{n} Schleifenebenen <20>bersprungen.
Die Syntax der \texttt{exit}-Anweisung lautet wie folgt:\nopagebreak
\LTXtable{\textwidth}{tab_exit.tex}
Die \texttt{exit}-Anweisung wird benutzt, um ein Skript zu beenden. Wenn der Parameter \textsl{n} angegeben wird, wird er von dem Skript als Exit-Code zur<75>ckgegeben.
Eines der markantesten Konzepte, das in Shell-Skripten benutzt wird, ist das der Datenstr<74>me. Die meisten der vielen Unix-Tools bieten die M<>glichkeit, Eingaben aus der sogenannten Standard-Eingabe entgegenzunehmen und Ausgaben dementsprechend auf der Standard-Ausgabe zu machen. Es gibt noch einen dritten Kanal f<>r Fehlermeldungen, so da<64> man eine einfache M<>glichkeit hat, fehlerhafte Programmdurchl<68>ufe zu behandeln indem man die Fehlermeldungen von den restlichen Ausgaben trennt.
Es folgt eine Aufstellung der drei Standardkan<61>le:\nopagebreak
Gerade der Mechanismus mit dem Piping sollte nicht untersch<63>tzt werden. Er dient nicht nur dazu, relativ kleine Texte zwischen Tools hin- und herzureichen. An dem folgenden Beispiel soll die M<>chtigkeit dieses kleinen Zeichens gezeigt werden:\nopagebreak
Es ist mit den passenden Tools unter Unix m<>glich, eine ganze Audio-CD mit zwei Befehlen an der Kommandozeile zu duplizieren. Das erste Kommando veranla<6C>t, da<64> die TOC (Table Of Contents) der CD in die Datei cd.toc geschrieben wird. Das dauert nur wenige Sekunden. Die Pipe steckt im zweiten Befehl. Hier wird der eigentliche Inhalt der CD mit dem Tool `cdparanoia' ausgelesen. Da kein Dateiname angegeben wird, schreibt cdparanoia die Daten auf seine Standard-Ausgabe. Diese wird von dem Brennprogramm `cdrdao' <20>bernommen und in Verbindung mit der TOC `on the fly' auf die CD geschrieben.\label{cdrdao}\nopagebreak