Shell-Programmierung/wie_sieht_ein_shell_skript_aus.tex
2004-11-19 12:09:34 +00:00

678 lines
44 KiB
TeX

% $Id$
\chapter{Wie sieht ein Shell-Skript aus?}
Wie schon erwähnt, kann ein Shell-Skript beinahe alles, was eine `richtige'
Programmiersprache auch kann. Bei der Entwicklung sollte man nur bedenken, daß
gerade die Ausführung von externen Kommandos~--~und das ist eine der
Standard-Techniken bei der Shell-Programmierung~--~nur sehr langsam vonstatten
geht. Für Anwendungen bei denen z. B. viele Rechnungen oder Stringbearbeitungen
gemacht werden müssen, sollte man also ggf. die Benutzung einer anderen
Sprache, beispielsweise Perl\index{Perl}, in Erwägung ziehen.
In der Shell stehen viele Mechanismen zur Verfügung, die auch aus anderen Sprachen bekannt sind. Um den Umfang dieses Dokuments nicht zu sprengen, werden an dieser Stelle nur die wichtigsten vorgestellt.
\section{Grundsätzliches}
Zunächst soll die Frage geklärt werden, wie man überhaupt ein ausführbares Shell-Skript schreibt. Dabei wird vorausgesetzt, daß dem Benutzer der Umgang mit mindestens einem Texteditor\index{Texteditor} (\texttt{vi}\index{vi=\texttt{vi}}, \texttt{emacs}\index{emacs=\texttt{emacs}} etc.) bekannt ist.
Bei der Erstellung oder Bearbeitung von Shell-Skripten muß darauf geachtet
werden, daß sich keine CR/LF-Zeilenumbrüche einschleichen, wie dies leicht bei
der Benutzung von MS-DOS bzw. Windows-Systemen zur Bearbeitung von Skripten
über das Netzwerk passieren kann.
\subsection{HowTo}
Zunächst muß mit Hilfe des Editors eine Textdatei angelegt werden, in die der `Quelltext' geschrieben wird. Wie der aussieht, sollte man anhand der folgenden Abschnitte und der Beispiele im Anhang erkennen können. Beim Schreiben sollte man nicht mit den Kommentaren\index{Kommentar} geizen, da ein Shell-Skript auch schon mal sehr unleserlich werden kann.
Nach dem Abspeichern der Datei unter einem geeigneten Namen\footnote{Bitte
\emph{nicht} den Namen \texttt{test}\index{test=\texttt{test}} verwenden. Es
existiert ein Unix-Systemkommando mit diesem Namen. Dieses steht fast immer
eher im Pfad, d. h. beim Kommando \texttt{test} würde nicht das eigene Skript
ausgeführt, sondern das Systemkommando. Dies ist einer der häufigsten und
zugleich einer der verwirrendsten Anfängerfehler. Mehr zu dem
\texttt{test}-Kommando unter \ref{bedingungen}.} muß die sie ausführbar gemacht
werden. Das geht mit dem Unix-Kommando
\texttt{chmod}\index{chmod=\texttt{chmod}}. Rechte können unter Unix getrennt
für den Benutzer (user, \texttt{u}), die Gruppe (group, \texttt{g}) oder andere
(others, \texttt{o}) vergeben werden. Außerdem kann man die Rechte für alle
Gruppen zusammen (all, a) setzen. Man kann getrennt die Rechte für das lesen
(read, \texttt{r}), das schreiben (write, \texttt{w}) und die Ausführung
(execution, \texttt{x}) vergeben. Damit die Datei für einen Benutzer wirklich
ausführbar ist, muß er das Lese- und das
Ausführungsrecht\index{Ausührungsrecht} haben. Um die Rechte zu setzen, muß man
\texttt{chmod} in Parametern mitgeben, worauf sich das Kommando bezieht, ob das
Recht gesetzt (\texttt{+}) oder weggenommen (\texttt{-}) werden soll, und
welche Rechte gemeint sind. Damit alle Benutzer das Skript ausführen dürfen,
benutzt man das Kommando \texttt{chmod ugo+rx name} oder einfach \texttt{chmod
+rx name}. Mit \texttt{chmod u+x name} hat nur der Besitzer der Datei
Ausführungsrechte.
Um ein Shell-Skript ausführen zu können braucht es aus der Sicht des
ausführenden Benutzers mindestens die Rechte zum Lesen (r) und Ausführen (x).
Dann kann das Skript gestartet werden. Da sich aus Sicherheitsgründen auf den meisten Systemen das aktuelle Verzeichnis nicht im Pfad des Benutzers befindet, muß man der Shell noch mitteilen, wo sie zu suchen hat: Mit \texttt{./name} wird versucht, im aktuellen Verzeichnis (\texttt{./}) ein Programm namens \texttt{name} auszuführen.
Auf den meisten Systemen befindet sich im Pfad ein Verweis auf das Verzeichnis
\texttt{bin} unterhalb des Home-Verzeichnisses eines Benutzers. Das bedeutet
daß man Skripte die immer wieder benutzt werden sollen dort ablegen kann, so
daß sie auch ohne eine Pfadangabe gefunden werden. Wie der Pfad genau aussieht
kann man an der Shell durch Eingabe von \texttt{echo
\$PATH}\index{\$PATH=\texttt{\$PATH}} herausfinden.
\subsection{Rückgabewerte}\label{exitcode}\index{Rückgabewert|(textbf}\index{Exit-Code|see{Rückgabewert}}\index{Exit-Status|see{Rückgabewert}}
Wenn unter Unix ein Prozeß beendet wird, gibt er einen Rückgabewert (auch
Exit-Code oder Exit-Status genannt) an seinen aufrufenden Prozeß zurück. So
kann der Mutterprozeß kontrollieren, ob die Ausführung des Tochterprozesses
ohne Fehler beendet wurde. In einigen Fällen (z. B.
\texttt{grep}\index{grep=\texttt{grep}}) werden unterschiedliche Exit-Codes für
unterschiedliche Ereignisse benutzt.
Dieser Rückgabewert wird bei der interaktiven Benutzung der Shell nur selten
benutzt, da Fehlermeldungen direkt vom Benutzer abgelesen werden können. Aber
in der Programmierung von Shell-Skripten ist er von unschätzbarem Wert. So kann
das Skript automatisch entscheiden, ob bestimmte Aktionen ausgeführt werden
sollen, die von anderen Aktionen ab\-hän\-gen. Beispiele dazu sieht man bei der
Beschreibung der Kommandos \texttt{if}\index{if=\texttt{if}} (\ref{if}),
\texttt{case}\index{case=\texttt{case}} (\ref{case}),
\texttt{while}\index{while=\texttt{while}} (\ref{while}) und
\texttt{until}\index{until=\texttt{until}} (\ref{until}), sowie in dem
Abschnitt über Befehlsformen (\ref{befehlsformen}).
In der Bourne-Shell wird der Exit-Code des letzten aufgerufenen Programms in
der Variable \texttt{\$?}\index{\$?=\texttt{\$?}} abgelegt. Üblicherweise geben
Programme den Wert 0 zurück, bei irgendwelchen Problemen einen von 0
verschiedenen Wert. Das wird im folgenden Beispiel deutlich:
\LTXtable{\textwidth}{tab_beisp_exitcode.tex}
Normalerweise wird man den Exit-Code nicht in dieser Form abfragen. Sinnvoller
ist folgendes Beispiel, in dem eine Datei erst gedruckt wird, und dann~--~falls
der Ausdruck erfolgreich war ~--~elöscht wird:
\LTXtable{\textwidth}{tab_beisp_exitcode_lpr.tex}
Näheres zur Verknüpfung von Aufrufen steht im Kapitel über Befehlsformen
(\ref{befehlsformen}). Beispiele zur Benutzung von Rückgabewerten in Schleifen
finden sich im Anhang unter \ref{beisp_schleifen_exitcode}.
Auch Shell-Skripte können einen Rückgabewert an aufrufende Prozesse
zurückgeben. Wie das geht, steht in dem Abschnitt zu \texttt{exit}
(\ref{exit}).
\index{Rückgabewert|)}
\section{Variablen}\index{Variablen|(textbf}
In einem Shell-Skript hat man~--~genau wie bei der interaktiven Nutzung der
Shell~--~Möglichkeiten, über Variablen zu verfügen. Anders als in den meisten
modernen Programmiersprachen gibt es aber keine Datentypen\index{Datentypen}
wie Ganzzahlen, Fließkommazahlen oder Strings\footnote{Bei einigen modernen
Shells (\texttt{csh}\index{C-Shell}, \texttt{tcsh}\index{TENEX-C-Shell},
\texttt{ksh}\index{Korn-Shell}, \texttt{bash}\index{Bourne-Again-Shell},
\texttt{zsh}\index{Z-Shell}...) hat man die Möglichkeit, Variablentypen zu
vereinbaren. In der Bourne-Shell\index{Bourne-Shell} nicht.}. Alle Variablen
werden als String gespeichert, wenn die Variable die Funktion einer Zahl
übernehmen soll, dann muß das verarbeitende Programm die Variable entsprechend
interpretieren\footnote{Für arithmetische Operationen steht das Programm
\texttt{expr}\index{expr=\texttt{expr}} zur Verfügung (siehe
Zählschleifen-Beispiel unter \ref{while})}.
Man muß bei der Benutzung von Variablen sehr aufpassen, wann die Variable
expandiert\footnote{Mit \emph{Expansion}\index{Expansion} ist das Ersetzen des
Variablennamens durch den Inhalt gemeint} wird und wann nicht. Grundsätzlich
werden Variablen während der Ausführung des Skriptes immer an den Stellen
ersetzt, an denen sie stehen. Das passiert in jeder Zeile, unmittelbar bevor
sie ausgeführt wird. Es ist also auch möglich, in einer Variable einen
Shell-Befehl abzulegen. Im Folgenden kann dann der Variablenname an der Stelle
des Befehls stehen. Um die Expansion einer Variable zu verhindern, benutzt man
das Quoting\index{Quoting} (siehe unter \ref{quoting}).
Wie aus diversen Beispielen hervorgeht, belegt man eine Variable, indem man dem
Namen mit dem Gleichheitszeichen einen Wert zuweist. Dabei darf zwischen dem
Namen und dem Gleichheitszeichen keine Leerstelle stehen, ansonsten erkennt die
Shell den Variablennamen nicht als solchen und versucht, ein gleichnamiges
Kommando auszuführen~--~was meistens durch eine Fehlermeldung quittiert wird.
Wenn man auf den Inhalt einer Variablen zugreifen möchte, leitet man den
Variablennamen durch ein \texttt{\$}-Zeichen ein. Alles was mit einem
\texttt{\$} anfängt wird von der Shell als Variable angesehen und entsprechend
behandelt (expandiert).
\index{Variablen|)}
\section{Vordefinierte Variablen}\label{vordefinierte_variablen}\index{Variablen}\index{vordefinierte Variablen}
\index{\$n=\texttt{\$}$n$|(textbf}
\index{\$*=\texttt{\$*}|(textbf}
\index{\$@=\texttt{\$@}|(textbf}
\index{\$\#=\texttt{\$\#}|(textbf}
\index{\$?=\texttt{\$?}|(textbf}
\index{\$\$=\texttt{\$\$}|(textbf}
\index{\$!!=\texttt{\$!!}|(textbf}
\index{\$ERRNO=\texttt{\$ERRNO}|(textbf}
\index{\$IFS=\texttt{\$IFS}|(textbf}
\index{\$PWD=\texttt{\$PWD}|(textbf}
\index{\$OLDPWD=\texttt{\$OLDPWD}|(textbf}
\index{ERRNO=\texttt{ERRNO}|see{\$ERRNO}}
\index{IFS=\texttt{IFS}|see{\$IFS}}
\index{PWD=\texttt{PWD}|see{\$PWD}}
\index{OLDPWD=\texttt{OLDPWD}|see{\$OLDPWD}}
Es gibt eine Reihe von vordefinierten Variablen, deren Benutzung ein
wesentlicher Bestandteil des Shell-Programmierens ist.
Die wichtigsten eingebauten Shell-Variablen sind:\nopagebreak
\LTXtable{\textwidth}{tab_vordefinierte_variablen.tex}
Die Variable \texttt{\$IFS} enthält per Default die Blank-Zeichen, also
Newline, Space und Tab. Man kann sie aber auch mit anderen Zeichen
überschreiben. Diese werden immer dann als Trennzeichen benutzt, wenn ein
String in mehrere Teile zerlegt werden soll, also beispielsweise in
\texttt{for}-Schleifen oder beim zeilenweisen Einlesen mit \texttt{read}. Ein
gutes Beispiel gibt es in dem Beispielskript zu \texttt{printf} (Abschnitt
\ref{printf}).
\texttt{\$ERRNO}, \texttt{\$PWD} und \texttt{\$OLDPWD} werden nicht von jeder
Shell gesetzt.
\index{\$n=\texttt{\$}$n$|)}
\index{\$*=\texttt{\$*}|)}
\index{\$@=\texttt{\$@}|)}
\index{\$\#=\texttt{\$\#}|)}
\index{\$?=\texttt{\$?}|)}
\index{\$\$=\texttt{\$\$}|)}
\index{\$!!=\texttt{\$!!}|)}
\index{\$ERRNO=\texttt{\$ERRNO}|)}
\index{\$IFS=\texttt{\$IFS}|)}
\index{\$PWD=\texttt{\$PWD}|)}
\index{\$OLDPWD=\texttt{\$OLDPWD}|)}
\section{Variablen-Substitution}\index{Variablen>-Substitution|(textbf}\index{Substitution|see{Variablen-Subst.}}\index{Variablen|(textbf}
\index{!==\texttt{!=}|(textbf}\index{\$\{Variable\}=\texttt{\$\{}\textsl{Variable}\texttt{\}}|(textbf}\index{\$\{Variable:-Wert\}=\texttt{\$\{}\textsl{Variable}\texttt{:-}\textsl{Wert}\texttt{\}}|(textbf}\index{\$\{Variable:=Wert\}=\texttt{\$\{}\textsl{Variable}\texttt{:=}\textsl{Wert}\texttt{\}}|(textbf}\index{\$\{Variable:?Wert\}=\texttt{\$\{}\textsl{Variable}\texttt{:?}\textsl{Wert}\texttt{\}}|(textbf}\index{\$\{Variable:+Wert\}=\texttt{\$\{}\textsl{Variable}\texttt{:+}\textsl{Wert}\texttt{\}}|(textbf}
Unter Variablen-Substitution versteht man verschiedene Methoden um die Inhalte
von Variablen zu benutzen. Das umfaßt sowohl die einfache Zuweisung eines
Wertes an eine Variable als auch einfache Möglichkeiten zur Fallunterscheidung.
In den fortgeschritteneren Shell-Versionen
(\texttt{bash}\index{Bourne-Again-Shell}, \texttt{ksh}\index{Korn-Shell})
existieren sogar Möglichkeiten, auf Substrings von Variableninhalten
zuzugreifen. In der Standard-Shell benutzt man für einfache Aufgaben
üblicherweise Tools wie \texttt{cut},
\texttt{basename}\index{basename=\texttt{basename}} oder \texttt{dirname};
komplexe Bearbeitungen erledigt der Stream-Editor
\texttt{sed}\index{sed=\texttt{sed}}. Einleitende Informationen dazu finden
sich im Kapitel über die Mustererkennung (\ref{mustererkennung}).
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ß die \textsl{Variable} einen Wert enthalten. \nopagebreak
\LTXtable{\textwidth}{tab_variablen_substitution.tex}
\medskip\emph{Beispiele:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_variablen_substitution.tex}
\index{!==\texttt{!=}|)}\index{\$\{Variable\}=\texttt{\$\{}\textsl{Variable}\texttt{\}}|)}\index{\$\{Variable:-Wert\}=\texttt{\$\{}\textsl{Variable}\texttt{:-}\textsl{Wert}\texttt{\}}|)}\index{\$\{Variable:=Wert\}=\texttt{\$\{}\textsl{Variable}\texttt{:=}\textsl{Wert}\texttt{\}}|)}\index{\$\{Variable:?Wert\}=\texttt{\$\{}\textsl{Variable}\texttt{:?}\textsl{Wert}\texttt{\}}|)}\index{\$\{Variable:+Wert\}=\texttt{\$\{}\textsl{Variable}\texttt{:+}\textsl{Wert}\texttt{\}}|)}
\index{Variablen>-Substitution|)}\index{Variablen|)}
\section{Quoting}\index{Quoting|(textbf}\label{quoting}
\index{Anführungszeichen|(textbf}\index{Ticks|(textbf}\index{Backslash|(textbf}\index{;=\texttt{;}|(textbf}\index{\&=\texttt{\&}|(textbf}\index{( )=\texttt{( )}|(textbf}\index{|=\texttt{|}|(textbf}\index{<=\texttt{<}|(textbf}\index{!>=\texttt{!>}|(textbf}\index{!>\&=\texttt{!>\&}|(textbf}\index{*=\texttt{*}|(textbf}\index{?=\texttt{?}|(textbf}\index{[ ]=\texttt{[ ]}|(textbf}\index{\~{}=\texttt{\~{}}|(textbf}\index{+=\texttt{+}|(textbf}\index{-=\texttt{-}|(textbf}\index{@=\texttt{@}|(textbf}\index{!!=\texttt{!!}|(textbf}\index{Backticks|(textbf}\index{\$=\texttt{\$}|(textbf}\index{[newline]=\texttt{[newline]}|(textbf}\index{[space]=\texttt{[space]}|(textbf}\index{[tab]=\texttt{[tab]}|(textbf}
\index{' '=\texttt{' '}|see{Ticks}}\index{` `=\texttt{` `}|see{Backticks}}\index{\dq~\dq=\texttt{\dq~\dq}|see{Anführungszeichen}}\index{\textbackslash=\texttt{\textbackslash}|see{Backslash}}
Dies ist ein sehr schwieriges Thema, da hier mehrere ähnlich aussehende Zeichen
völlig verschiedene Effekte bewirken. Die Bourne-Shell unterscheidet allein
zwischen drei verschiedenen Anführungszeichen. Das Quoten dient dazu, bestimmte
Zeichen mit einer Sonderbedeutung vor der Shell zu `verstecken' um zu
verhindern, daß diese expandiert (ersetzt) werden.
Die folgenden Zeichen haben eine spezielle Bedeutung innerhalb der Shell:\nopagebreak
\LTXtable{\textwidth}{tab_quoting_sonderzeichen.tex}
Die folgenden Zeichen können zum Quoten verwendet werden:\nopagebreak
\LTXtable{\textwidth}{tab_quoting_zeichen.tex}
\medskip\emph{Beispiele:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_quoting.tex}
\index{Anführungszeichen|)}\index{Ticks|)}\index{Backslash|)}\index{;=\texttt{;}|)}\index{\&=\texttt{\&}|)}\index{( )=\texttt{( )}|)}\index{|=\texttt{|}|)}\index{<=\texttt{<}|)}\index{!>=\texttt{!>}|)}\index{!>\&=\texttt{!>\&}|)}\index{*=\texttt{*}|)}\index{?=\texttt{?}|)}\index{[ ]=\texttt{[ ]}|)}\index{\~{}=\texttt{\~{}}|)}\index{+=\texttt{+}|)}\index{-=\texttt{-}|)}\index{@=\texttt{@}|)}\index{!!=\texttt{!!}|)}\index{Backticks|)}\index{\$=\texttt{\$}|)}\index{[newline]=\texttt{[newline]}|)}\index{[space]=\texttt{[space]}|)}\index{[tab]=\texttt{[tab]}|)}
\index{Quoting|)}
\section{Meta-Zeichen}\index{Meta-Zeichen|(textbf}\index{Wildcards|see{Metazeichen}}\index{Joker-Zeichen|see{Metazeichen}}\index{Platzhalter|see{Metazeichen}}
\index{*=\texttt{*}|(textbf}\index{?=\texttt{?}|(textbf}\index{[abc]=\texttt{[}\textsl{abc}\texttt{]}|(textbf}\index{[a-q]=\texttt{[}\textsl{a}\texttt{-}\textsl{q}\texttt{]}|(textbf}\index{[!!abc]=\texttt{[!!}\textsl{abc}\texttt{]}|(textbf}\index{Dateinamen|(textbf}
\index{\~{}=\texttt{\~{}}|(textbf}\index{\~{}name=\texttt{\~{}}\textsl{name}|(textbf}\index{\~{}+=\texttt{\~{}+}|(textbf}\index{\~{}-=\texttt{\~{}-}|(textbf}
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.
Die wichtigsten Meta-Zeichen sind:\nopagebreak
\LTXtable{\textwidth}{tab_metazeichen.tex}
\texttt{\~}, \texttt{\~{}}\textsl{name}, \texttt{\~{}+} und \texttt{\~{}-}
werden nicht von jeder Shell unterstützt.
\medskip\emph{Beispiele:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_metazeichen.tex}
Hier ist anzumerken, daß Hidden Files (Dateien, deren Name mit einem Punkt
beginnt) nicht durch ein einfaches \texttt{*} erfaßt werden, sondern nur durch
das Suchmuster \texttt{.*}.
\index{*=\texttt{*}|)}\index{?=\texttt{?}|)}\index{[abc]=\texttt{[}\textsl{abc}\texttt{]}|)}\index{[a-q]=\texttt{[}\textsl{a}\texttt{-}\textsl{q}\texttt{]}|)}\index{[!!abc]=\texttt{[!!}\textsl{abc}\texttt{]}|)}\index{Dateinamen|)}
\index{\~{}=\texttt{\~{}}|)}\index{\~{}name=\texttt{\~{}}\textsl{name}|)}\index{\~{}+=\texttt{\~{}+}|)}\index{\~{}-=\texttt{\~{}-}|)}
\index{Meta-Zeichen|)}
\section{Mustererkennung}\index{Mustererkennung|(textbf}\label{mustererkennung}
\index{ed=\texttt{ed}|(textbf}\index{ex=\texttt{ex}|(textbf}\index{vi=\texttt{vi}|(textbf}\index{sed=\texttt{sed}|(textbf}\index{awk=\texttt{awk}|(textbf}\index{grep=\texttt{grep}|(textbf}\index{egrep=\texttt{egrep}|(textbf}
\index{*=\texttt{*}|(textbf}\index{.=\texttt{.}|(textbf}\index{\^=\texttt{\^}|(textbf}\index{\$=\texttt{\$}|(textbf}\index{Backslash|(textbf}\index{[ ]=\texttt{[ ]}|(textbf}\index{\textbackslash( \textbackslash)=\texttt{\textbackslash( \textbackslash)}|(textbf}\index{\textbackslash\{ \textbackslash\}=\texttt{\textbackslash\{ \textbackslash\}}|(textbf}\index{\textbackslash< \textbackslash>=\texttt{\textbackslash< \textbackslash>}|(textbf}\index{+=\texttt{+}|(textbf}\index{?=\texttt{?}|(textbf}\index{|=\texttt{|}|(textbf}\index{( )=\texttt{( )}|(textbf}\index{Regulärer Ausdruck|(textbf}
\index{\textbackslash n=\texttt{\textbackslash}\textsl{n}|(textbf}\index{\&=\texttt{\&}|(textbf}\index{\~{}=\texttt{\~{}}|(textbf}\index{\textbackslash u=\texttt{\textbackslash u}|(textbf}\index{\textbackslash U=\texttt{\textbackslash U}|(textbf}\index{\textbackslash l=\texttt{\textbackslash l}|(textbf}\index{\textbackslash L=\texttt{\textbackslash L}|(textbf}\index{\textbackslash E=\texttt{\textbackslash E}|(textbf}\index{\textbackslash e=\texttt{\textbackslash e}|(textbf}
Man unterscheidet in der Shell-Programmierung zwischen den
Meta-Zeichen\index{Meta-Zeichen}, die bei der Bezeichnung von Dateinamen
eingesetzt werden (sogenanntes `Globbing'\index{Globbing}) und den
Meta-Zeichen, die in mehreren Programmen Verwendung finden, um z. B. Suchmuster
zu definieren. Diese Muster werden auch reguläre Ausdrücke (regular
expression)\index{Regular Expression|see{Regulärer
Ausdruck}}\index{Expression|see{Regulärer
Ausdruck}}\index{Ausdruck|see{Regulärer Ausdruck}} genannt. Sie bieten
wesentlich mehr Möglichkeiten als die relativ einfachen Wildcards für
Dateinamen.
In der folgenden Tabelle wird gezeigt, in welchen Unix-Tools welche Zeichen zur Verfügung stehen. Eine ausführlichere Beschreibung der Einträge findet sich auf Seite \pageref{beschreibung_der_muster}. \nopagebreak
\LTXtable{\textwidth}{tab_mustererkennung_muster.tex}
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
\LTXtable{\textwidth}{tab_mustererkennung_ersatzmuster.tex}
\medskip\emph{Sonderzeichen in Suchmustern:}\label{beschreibung_der_muster}\nopagebreak
\LTXtable{\textwidth}{tab_mustererkennung_sonderzeichen.tex}
\medskip\emph{Sonderzeichen in Ersatzmustern:}\nopagebreak
\LTXtable{\textwidth}{tab_mustererkennung_ersatzsonderzeichen.tex}
\medskip\emph{Beispiele:} Muster\nopagebreak
\LTXtable{\textwidth}{tab_beisp_muster.tex}
\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
\LTXtable{\textwidth}{tab_beisp_sed-ex.tex}
\index{ed=\texttt{ed}|)}\index{ex=\texttt{ex}|)}\index{vi=\texttt{vi}|)}\index{sed=\texttt{sed}|)}\index{awk=\texttt{awk}|)}\index{grep=\texttt{grep}|)}\index{egrep=\texttt{egrep}|)}
\index{*=\texttt{*}|)}\index{.=\texttt{.}|)}\index{\^=\texttt{\^}|)}\index{\$=\texttt{\$}|)}\index{Backslash|)}\index{[ ]=\texttt{[ ]}|)}\index{\textbackslash( \textbackslash)=\texttt{\textbackslash( \textbackslash)}|)}\index{\textbackslash\{ \textbackslash\}=\texttt{\textbackslash\{ \textbackslash\}}|)}\index{\textbackslash< \textbackslash>=\texttt{\textbackslash< \textbackslash>}|)}\index{+=\texttt{+}|)}\index{?=\texttt{?}|)}\index{|=\texttt{|}|)}\index{( )=\texttt{( )}|)}\index{Regulärer Ausdruck|)}
\index{\textbackslash n=\texttt{\textbackslash}\textsl{n}|)}\index{\&=\texttt{\&}|)}\index{\~{}=\texttt{\~{}}|)}\index{\textbackslash u=\texttt{\textbackslash u}|)}\index{\textbackslash U=\texttt{\textbackslash U}|)}\index{\textbackslash l=\texttt{\textbackslash l}|)}\index{\textbackslash L=\texttt{\textbackslash L}|)}\index{\textbackslash E=\texttt{\textbackslash E}|)}\index{\textbackslash e=\texttt{\textbackslash e}|)}
\index{Mustererkennung|)}
\section{Klammer-Expansion\label{klammerexpansion}\index{Klammer-Expansion|(textbf}\index{Brace Expansion|see{Klammer-Expansion}}}
Dieser Mechanismus ist sehr praktisch, aber nur wenigen Programmierern bekannt.
Er steht nicht in jeder Shell zur Verfügung.
Über die Klammer-Expansion (Brace Expansion) können automatisch Strings
generiert werden. Dabei wird ein Muster angegeben, nach dem neue Strings
aufgebaut werden. Dieses Muster besteht aus einem Prefix, der allen erzeugten
Strings vorangestellt wird, und einer in geschweifte Klammern eingebundenen und
durch Komma getrennten Menge von String-Teilen. Dieses Konstrukt expandiert zu
mehreren, durch Leerzeichen getrennten Strings, indem sämtliche möglichen
Permutationen generiert werden.
Die durch die Klammern angegebenen Mengen können auch verschachtelt werden.
Dabei werden die Klammern von links nach rechts aufgelöst. Die Ergebnisse
werden nicht sortiert, sondern in der Reihenfolge ihrer Erstellung
zurückgegeben werden.
Die Expansion von Klammern erfolgt vor der Behandlung aller anderen
Ersetzungen. Auch eventuell vorhandenen Sonderzeichen bleiben in dem
expandierten Text erhalten. So lassen sich auch Variablen durch diese Technik
erzeugen.
\medskip\emph{Beispiele:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_klammerexpansion.tex}
\index{Klammer-Expansion|)}
\section{Arithmetik-Expansion\label{arithmetikexpansion}\index{Arithmetik-Expansion|(textbf}}
Auch hier werden Klammern expandiert. Allerdings gleich doppelte Klammern. Mit
einem Konstrukt in der Form \texttt{i=\$((\$i + 1))} können einfache
Berechnungen angestellt werden.
Dabei wird der Ausdruck in den Klammern bewertet als ob er in doppelten
An\-füh\-rungs\-zei\-chen stehen würde. Das bedeutet zum Einen, daß man auch
mit Variablen rechnen kann, zum anderen macht es das Quoten des Sternchens
überflüssig.
Für komplexere Berechnungen steht das Tool \texttt{bc} (Siehe Abschnitt
\ref{bc}) zur Ver\-\-gung.
\index{Arithmetik-Expansion|)}
\section{Programmablaufkontrolle}
Bei der Shell-Programmierung verfügt man über ähnliche Konstrukte wie bei anderen Programmiersprachen, um den Ablauf des Programms zu steuern. Dazu gehören Funktionsaufrufe, Schleifen, Fallunterscheidungen und dergleichen.\nopagebreak
\subsection{Kommentare\label{kommentare}\index{Kommentar|(textbf} (\texttt{\#})}\index{\#=\texttt{\#}|see{Kommentar}}
Kommentare in der Shell beginnen immer mit dem Nummern-Zeichen (\verb\#\).
Dabei spielt es keine Rolle, ob das Zeichen am Anfang der Zeile steht, oder
hinter irgendwelchen Befehlen. Alles von diesem Zeichen bis zum Zeilenende wird
nicht beachtet (bis auf eine Ausnahme~--~siehe unter \ref{auswahl_der_shell}).
\index{Kommentar|)}
\subsection{Auswahl der Shell (\texttt{\#!})}\label{auswahl_der_shell}\index{\#!!=\texttt{\#!!}|see{Shell / Auswahl der\ldots}}\index{Shell>Auswahl der\ldots|(textbf}
In der ersten Zeile eines Shell-Skriptes sollte definiert werden, mit welchem
Programm das Skript ausgeführt werden soll. Das System öffnet dann eine
Subshell\index{Subshell} und führt das restliche Skript in dieser aus.
Die Angabe erfolgt über eine Zeile in der Form \verb\#!/bin/sh\, wobei unter
\verb\/bin/sh\ die entsprechende Shell (in diesem Fall die Bourne-Shell) liegt.
Dieser Eintrag wirkt nur dann, wenn er in der ersten Zeile und der ersten
Spalte des Skripts steht.
Dieser Mechanismus ist bei der Bourne-Shell nicht vorhanden, er wurde mit den
moderneren Shells eingeführt um eben durch die Angabe von \verb\#!/bin/sh\ die
Bourne-Shell für die Ausführung von Shell-Skripten benutzen zu können. In der
Bourne-Shell wirkt das führende \verb\#\ als Kommentarzeichen.
\index{Shell>Auswahl der\ldots|)}
\subsection{Null-Befehl (\texttt{:})}\label{null-befehl}\index{Null-Befehl|(textbf}\index{:=\texttt{:}|see{Null-Befehl}}\index{Doppelpunkt|see{Null-Befehl}}
Dieser Befehl tut nichts, außer den Status 0 zurückzugeben. Er wird benutzt, um Endlosschleifen\index{Endlosschleife} zu schreiben (siehe unter \ref{while}), oder um leere Blöcke in \texttt{if}- oder \texttt{case}-Konstrukten\index{if=\texttt{if}}\index{case=\texttt{case}} möglich zu machen.
\medskip\emph{Beispiel:} Prüfen, ob jemand angemeldet ist:\nopagebreak
\LTXtable{\textwidth}{tab_beisp_nullbefehl.tex}
\index{Null-Befehl|)}
\subsection{Source (\texttt{.})}\label{source}\index{source=\texttt{source}|(textbf}\index{.=\texttt{.}|see{source}}
Ein Shell-Skript kann in keiner Weise Einfluß auf die umgebende Shell nehmen. Das heißt, daß es beispielsweise nicht möglich ist, in einem Skript Variablen zu setzen, die dann in der aufrufenden Shell zur Verfügung stehen. Genauso wenig ist es möglich, daß ein Skript den Pfad ändert, in dem man sich befindet. Der Grund für dieses Verhalten ist die Systemsicherheit. Man will verhindern, daß ein Skript unbemerkt Änderungen an der Benutzerumgebung vornimmt.
Wenn es aber doch gewünscht wird, daß ein Skript die Umgebung des Benutzers ändern kann, dann muß es mit dem Source-Kommando aufgerufen werden. Das wird in der Form \verb\source skriptname\ bzw. \verb\. skriptname\ angegeben. Er bewirkt ähnliches wie ein \verb\#include\ in der Programmiersprache C.
Die `gesourcte' Datei wird eingelesen und ausgefü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ötigte Funktionen (Starten eines Dienstes, Statusmeldungen auf dem Bildschirm etc.) in einer zentralen Datei pflegen zu können (siehe Beispiel unter~\ref{init-skript}).
\index{source=\texttt{source}|)}
\subsection{Funktionen}\label{funktionen}\index{Funktion|(textbf}
Es ist in der Shell auch möglich, ähnlich wie in einer `richtigen' Programmiersprache Funktionen zu deklarieren und zu benutzen. Da die Bourne-Shell (\verb\sh\) nicht über Aliase\index{Aliase} verfügt, können einfache Funktionen als Ersatz dienen.
Der Rückgabewert einer Funktion ist gleich dem Rückgabewert des letzten in der
Funktion aufgerufenen Kommandos, es sei denn man gibt mittels
\verb\return\ (Siehe \ref{return}) explizit einen anderen Wert zurück.
\medskip\emph{Beispiel:} Die Funktion gibt die Anzahl der Dateien im aktuellen
Verzeichnis aus. Aufgerufen wird diese Funktion wie ein Befehl, also einfach
durch die Eingabe von \verb\count\.\nopagebreak
\LTXtable{\textwidth}{tab_beisp_funktionen.tex}
\index{Funktion|)}
\subsection{Bedingungen (\texttt{[ ]})}\label{bedingungen}\index{Bedingungen|see{test}}\index{[ ]=\texttt{[ ]}|see{test}}\index{test=\texttt{test}|(textbf}
Da die Standard-Shell keine arithmetischen oder logischen Ausdrücke auswerten
kann\footnote{\texttt{if} und Konsorten prüfen nur den
Rückgabewert\index{Rückgabewert} eines aufgerufenen Programmes~--~0 bedeutet
`true', alles andere bedeutet `false', siehe auch \ref{exitcode}.}, muß dazu
ein externes Programm benutzt werden. Dieses Programm heißt
\verb\test\\index{test=\texttt{test}}. Üblicherweise besteht auf allen Systemen
auch noch ein Link namens \verb\[\ auf dieses Programm. Dieser Link ist fast
absolut gleichwertig zu benutzen (in dieser Form wird allerdings eine
abschließende Klammer nach der Bedingung erwartet). Dementsprechend ist es auch
zwingend erforderlich, nach der Klammer ein Leerzeichen zu schreiben. Das dient
dazu, Bedingungen in \verb\if\-Abfragen u. ä. lesbarer zu machen.
Das \verb\test\-Programm bietet sehr umfangreiche Optionen an. Dazu gehören
Dateitests und Vergleiche von Zeichenfolgen oder ganzen Zahlen. Diese
Bedingungen können auch durch Verknüpfungen kombiniert werden.
\medskip\medskip\emph{Dateitests:}\index{Dateitests}\nopagebreak
\LTXtable{\textwidth}{tab_bedingungen_dateitests.tex}
\medskip\emph{Bedingungen für Zeichenfolgen:}\nopagebreak
\LTXtable{\textwidth}{tab_bedingungen_zeichenfolgen.tex}
\medskip\emph{Ganzzahlvergleiche:}\nopagebreak
\LTXtable{\textwidth}{tab_bedingungen_ganzzahlvergleiche.tex}
\medskip\emph{Kombinierte Formen:}\nopagebreak
\LTXtable{\textwidth}{tab_bedingungen_kombinationen.tex}
\medskip\emph{Beispiele:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_bedingungen.tex}
\index{test=\texttt{test}|)}
\subsection{if\ldots}\label{if}\index{if=\texttt{if}|(textbf}\index{then=\texttt{then}|see{if}}\index{elif=\texttt{elif}|see{if}}\index{else=\texttt{else}|see{if}}\index{fi=\texttt{fi}|see{if}}
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ängig.
Die Syntax der \texttt{if}-Anweisung lautet wie folgt:\nopagebreak
\LTXtable{\textwidth}{tab_if.tex}
Wenn die \textsl{Bedingung1} erfüllt ist, werden die \textsl{Befehle1} ausgeführt; andernfalls, wenn die \textsl{Bedingung2} erfüllt ist, werden die \textsl{Befehle2} ausgeführt. Trifft keine Bedingung zu, sollen die \textsl{Befehle3} ausgefü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ü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ügung.
\medskip\emph{Beispiele:} Man achte auf die Positionierung der Semikola\footnote{Und man verzeihe mir einen eventuell falschen Plural\ldots :-)}.\nopagebreak
\LTXtable{\textwidth}{tab_beisp_if.tex}
\index{if=\texttt{if}|)}
\subsection{case\ldots}\label{case}\index{case=\texttt{case}|(textbf}\index{esac=\texttt{esac}|see{case}}
\index{;;=\texttt{;;}|see{case}}
Auch die \texttt{case}-Anweisung ist vergleichbar in vielen anderen Sprachen vorhanden. Sie dient, ä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} übereinstimmt, wird die entsprechende Befehlsfolge\index{Befehls>-folge} (\textsl{Befehle1}) ausgeführt, bei Übereinstimmung mit \textsl{Muster2} werden die Kommandos der zweiten Befehlsfolge\index{Befehls>-folge} (\textsl{Befehle2}) ausgeführt, usw. Der letzte Befehl in jeder Gruppe muß mit \texttt{;;} gekennzeichnet werden. Das bedeutet für die Shell soviel wie `springe zum nächsten \texttt{esac}', so daß die anderen Bedingungen nicht mehr überprüft werden.
In den Mustern sind die gleichen Meta-Zeichen\index{Meta-Zeichen} erlaubt wie bei der Auswahl von Dateinamen. Das bedeutet, daß 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.
\medskip\emph{Beispiele:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_case.tex}
\index{case=\texttt{case}|)}
\subsection{for\ldots}\label{for}\index{for=\texttt{for}|(textbf}\index{Schleife>for-=\texttt{for}-|see{for}}\index{in=\texttt{in}|see{for}}\index{in=\texttt{in}|see{case}}\index{do=\texttt{do}|see{for}}\index{do=\texttt{do}|see{while}}\index{do=\texttt{do}|see{until}}\index{do=\texttt{done}|see{for}}\index{do=\texttt{done}|see{while}}\index{do=\texttt{done}|see{until}}
Dieses Konstrukt ähnelt nur auf den ersten Blick seinen Pendants aus anderen
Programmiersprachen. In anderen Sprachen wird die \texttt{for}-Schleife
meistens dazu benutzt, eine Zählvariable über einen bestimmten Wertebereich
iterieren zu lassen (\texttt{for i = 1 to 100\ldots next}). In der Shell
dagegen wird die Laufvariable nicht mit aufeinanderfolgenden Zahlen belegt,
sondern mit einzelnen Werten aus einer anzugebenden Liste\footnote{Wenn man
trotzdem eine Laufvariable\index{Laufvariable} braucht, muß man dazu die
\texttt{while}-Schleife\index{while=\texttt{while}} `mißbrauchen' (siehe unter
\ref{while}).}.
Die Syntax der \texttt{for}-Schleife lautet wie folgt:\nopagebreak
\LTXtable{\textwidth}{tab_for.tex}
Die \textsl{Befehle} werden ausgeführt, wobei der Variablen \textsl{x}
nacheinander die Werte aus der \textsl{Liste} zugewiesen werden. Wie man sieht
ist die Angabe der \textsl{Liste} optional, wenn sie nicht angegeben wird,
nimmt \textsl{x} der Reihe nach alle Werte aus \texttt{\$@} (in dieser
vordefinierten Variablen liegen die Aufrufparameter~--~siehe unter
\ref{vordefinierte_variablen}) an. Wenn die Ausfü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{Beispiele:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_for.tex}
\index{for=\texttt{for}|)}
\subsection{while\ldots}\label{while}\index{while=\texttt{while}|(textbf}\index{Schleife>while-=\texttt{while}-|see{while}}
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ührt, wie die \textsl{Bedingung} erfüllt ist. Dabei wird die \textsl{Bedingung} vor der Ausführung der \textsl{Befehle} überprüft. Die \textsl{Bedingung} wird dabei ü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ü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
Zählschleife\index{Zählschleife}\index{Schleife>Zähl-=Zähl-|see{Zählschleife}}.
In anderen Sprachen kann man mit der
\texttt{for}-Schleife\index{for=\texttt{for}} eine
Laufvariable\index{Laufvariable} über einen bestimmten Wertebereich iterieren
lassen (\texttt{for i = 1 to 100...next}). Da das mit der \texttt{for}-Schleife
der Shell nicht geht\footnote{Auf einigen Systemen steht für diesen Zweck auch
das Kommando \texttt{seq} (Siehe Abschnitt \ref{seq}) zur Verfügung.}, ersetzt
man die Funktion durch geschickte Anwendung der
\texttt{while}-Schleife:\nopagebreak
\LTXtable{\textwidth}{tab_beisp_while_for.tex}
\index{while=\texttt{while}|)}
\subsection{until\ldots}\label{until}\index{until=\texttt{until}|(textbf}\index{Schleife>until-=\texttt{until}-|see{until}}
Die \texttt{until}-Schleife ist das Gegenstück zur \texttt{while}-Schleife.
Allerdings nicht in dem Sinn, wie sie in den meisten anderen
Programmiersprachen verstanden wird. Sie arbeitet in der Shell genau wie die
\texttt{while}-Schleife, mit dem Unterschied daß die Bedingung negiert wird.
Es ist also auch eine kopfgesteuerte Schleife, die allerdings so lange läuft
wie die angegebene Bedingung nicht zutrifft.
Die Syntax der \texttt{until}-Schleife lautet wie folgt:\nopagebreak
\LTXtable{\textwidth}{tab_until.tex}
Die \textsl{Bedingung} wird dabei ü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 Aus\-füh\-rung 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:} Hier wird die Bedingung nicht per \texttt{test} sondern mit dem Rückgabewert\index{Rückgabewert} des Programms \texttt{grep}\index{grep=\texttt{grep}} formuliert.\nopagebreak
\LTXtable{\textwidth}{tab_beisp_until.tex}
\index{until=\texttt{until}|)}
\subsection{continue}\label{continue}\index{continue=\texttt{continue}|(textbf}
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 überspringen und mit dem nächsten Schleifendurchlauf anzufangen. Wenn der Parameter \textsl{n} angegeben wird, werden \textsl{n} Schleifenebenen übersprungen.
\index{continue=\texttt{continue}|)}
\subsection{break}\label{break}\index{break=\texttt{break}|(textbf}
Die Syntax der \texttt{break}-Anweisung lautet wie folgt:\nopagebreak
\LTXtable{\textwidth}{tab_break.tex}
Mit \texttt{break} kann man die innerste Ebene (bzw. \textsl{n} Schleifenebenen) verlassen ohne den Rest der Schleife auszuführen.
\index{break=\texttt{break}|)}
\subsection{exit}\label{exit}\index{exit=\texttt{exit}|(textbf}
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ückgegeben.
\index{exit=\texttt{exit}|)}
\subsection{return}\label{return}\index{return=\texttt{return}|(textbf}
Die Syntax der \texttt{return}-Anweisung lautet wie folgt:\nopagebreak
\LTXtable{\textwidth}{tab_return.tex}
Mittels \texttt{return} kann eine Funktion (siehe \ref{funktionen}) einen
bestimmten Wert zurückgeben. Anderenfalls wird der Exit-Code des letzten in der
Funktion ausgeführten Befehls zurückgegeben.
\index{return=\texttt{return}|)}
\section{Befehlsformen}\label{befehlsformen}\index{Befehls>-formen|(textbf}
\index{\&=\texttt{\&}|(textbf}\index{;=\texttt{;}|(textbf}\index{( )=\texttt{( )}|(textbf}\index{\{ \}=\texttt{\{ \}}|(textbf}\index{Pipe|(textbf}\index{Backticks|(textbf}\index{\&\&=\texttt{\&\&}|(textbf}\index{!|!|=\texttt{!|!|}|(textbf}\index{Befehls>-substitution|(textbf}\index{Befehls>-folge|(textbf}\index{Befehls>-block|(textbf}
\index{!|=\texttt{!|}|see{Pipe}}\index{Substitution|see{Befehls-Subst.}}
Es gibt eine Reihe verschiedener Möglichkeiten, Kommandos auszuführen:\nopagebreak
\LTXtable{\textwidth}{tab_befehlsformen.tex}
\medskip\emph{Beispiele:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_befehlsformen.tex}
\index{\&=\texttt{\&}|)}\index{;=\texttt{;}|)}\index{( )=\texttt{( )}|)}\index{\{ \}=\texttt{\{ \}}|)}\index{Pipe|)}\index{Backticks|)}\index{\&\&=\texttt{\&\&}|)}\index{!|!|=\texttt{!|!|}|)}\index{Befehls>-substitution|)}\index{Befehls>-folge|)}\index{Befehls>-block|)}
\index{Befehls>-formen|)}
\section{Datenströme}\label{datenstrom}\index{Datenströme|(textbf}
\index{<=\texttt{<}|(textbf}\index{<<=\texttt{<<}|(textbf}\index{!>\&=\texttt{!>\&}|(textbf}\index{!>\&-=\texttt{!>\&-}|(textbf}\index{<\&=\texttt{<\&}|(textbf}\index{<\&-=\texttt{<\&-}|(textbf}\index{!>=\texttt{!>}|(textbf}\index{!>!>=\texttt{!>!>}|(textbf}\index{Pipe|(textbf}\index{Dateideskriptor|(textbf}\index{Standard-Eingabe|(textbf}\index{Standard-Ausgabe|(textbf}\index{Standard-Fehlerausgabe|(textbf}\index{Here-Dokument|(textbf}
\index{Deskriptor|see{Dateideskriptor}}\index{Ausgabe|see{Standard-Ausgabe}}\index{Fehlerausgabe|see{Standard-Fehlerausgabe}}\index{Fehlermeldungen|see{Standard-Fehlerausgabe}}\index{stdin|see{Standard-Eingabe}}\index{stdout|see{Standard-Ausgabe}}\index{stderr|see{Standard-Fehlerausgabe}}
Eines der markantesten Konzepte, das in Shell-Skripten benutzt wird, ist das der Datenströ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ß man eine einfache Möglichkeit hat, fehlerhafte Programmdurchläufe zu behandeln indem man die Fehlermeldungen von den restlichen Ausgaben trennt.
Es folgt eine Aufstellung der drei Standardkanäle:\nopagebreak
\LTXtable{\textwidth}{tab_datenstroeme_kanaele.tex}
Die standardmäßige Eingabequelle oder das Ausgabeziel können wie folgt geändert werden:
\emph{Einfache Umlenkung:}\nopagebreak\index{Umlenkung}
\LTXtable{\textwidth}{tab_datenstroeme_einfach.tex}
Die Technik eines Here-Dokuments ist sicherlich auf den ersten Blick etwas
verwirrend. Man benutzt Here-Dokumente zum Beispiel in einer Situation, in der
ein fest vorgegebener Text benötigt wird. Man stelle sich ein Skript vor, das
jeden Tag eine Mail mit festem Inhalt und variablem Anhang verschickt.
Oder eine eingebaute Hilfe-Funktion, die bei falschen Parametern einen
Hilfetext ausgibt.
Natürlich könnte man zu diesem Zweck eine eigene Datei einrichten, aber das ist
eigentlich nicht notwendig. Man handelt sich nur Ärger ein, wenn man das Skript
auf einen anderen Rechner portiert und die Datei vergißt. Abgesehen davon - wo
legt man eine solche Datei sinnvoll ab?
Um diesem Ärger zu entgehen, sollte man in einer solchen Situation ein
Here-Do\-ku\-ment benutzen.
\emph{Umlenkung mit Hilfe von Dateideskriptoren:}\nopagebreak
\LTXtable{\textwidth}{tab_datenstroeme_deskriptoren.tex}
\emph{Mehrfach-Umlenkung:}\index{Umlenkung}\index{Mehrfach-Umlenkung}\nopagebreak
\LTXtable{\textwidth}{tab_datenstroeme_mehrfach.tex}
Zwischen den Dateideskriptoren und einem Umlenkungssymbol darf kein Leerzeichen sein; in anderen Fällen sind Leerzeichen erlaubt.
\medskip\emph{Beispiele:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_datenstroeme.tex}
\medskip\emph{Beispiel eines Here-Dokuments:}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_datenstroeme_here-dokument.tex}
Gerade der Mechanismus mit dem Piping sollte nicht unterschä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ßt, daß 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' übernommen und in Verbindung mit der TOC `on the fly' auf die CD geschrieben.\label{cdrdao}\nopagebreak
\LTXtable{\textwidth}{tab_beisp_datenstroeme_cdparanoia.tex}
\index{<=\texttt{<}|)}\index{!>\&=\texttt{!>\&}|)}\index{!>\&-=\texttt{!>\&-}|)}\index{<\&=\texttt{<\&}|)}\index{<\&-=\texttt{<\&-}|)}\index{!>=\texttt{!>}|)}\index{!>!>=\texttt{!>!>}|)}\index{Pipe|)}\index{Dateideskriptor|)}\index{Standard-Eingabe|)}\index{Standard-Ausgabe|)}\index{Standard-Fehlerausgabe|)}
\index{Datenströme|)}