\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. \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. 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 der Eintrag \texttt{\~{}/bin} bzw. \texttt{/home/benutzername/bin}, 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. 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 --- gelö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{\$PWD=\texttt{\$PWD}|(textbf}\index{\$OLDPWD=\texttt{\$OLDPWD}|(textbf} \index{ERRNO=\texttt{ERRNO}|see{\$ERRNO}}\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} \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{\$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. Unix 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} \medskip\emph{Beispiele:}\nopagebreak \LTXtable{\textwidth}{tab_beisp_metazeichen.tex} \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{Klammer-Expansion} man bash /Brace Expansion TODO!!! \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{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 welcher Shell 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 des Skripts steht. \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}\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. \medskip\emph{Beispiel:} Die Funktion gibt die Anzahl der Dateien im aktuellen Verzeichnis zurück. 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}}\index{Schleife>Fußgesteuerte-|see{while}}\index{Fußgesteuerte Schleife|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}\index{seq=\texttt{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}}\index{Schleife>Kopfgesteuerte-|see{until}}\index{Kopfgesteuerte Schleife|see{until}} Die \texttt{until}-Schleife ist das Gegenstück zur \texttt{while}-Schleife: die ebenfalls aus vielen anderen Sprachen bekannte Fußgesteuerte Schleife. Die Syntax der \texttt{until}-Schleife lautet wie folgt:\nopagebreak \LTXtable{\textwidth}{tab_until.tex} Die \textsl{Befehle} werden ausgeführt, bis die \textsl{Bedingung} erfüllt ist. Dabei wird die \textsl{Bedingung} nach 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:} 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}|)} \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-Dokument 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|)}