Shell-Programmierung/wie_sieht_ein_shell_skript_aus.tex

1100 lines
55 KiB
TeX
Raw Permalink Normal View History

2003-04-11 15:05:25 +00:00
% $Id$
2001-07-02 12:52:18 +00:00
\chapter{Wie sieht ein Shell-Skript aus?}
2004-11-12 12:07:32 +00:00
Wie schon erw<72>hnt, kann ein Shell-Skript beinahe alles, was eine `richtige'
Programmiersprache auch kann. Bei der Entwicklung sollte man nur bedenken, da<64>
2004-12-02 13:54:06 +00:00
gerade die Aus\-f<EFBFBD>h\-rung von externen Kommandos~--~und das ist eine der
Standard-Techniken bei der Shell-Pro\-gram\-mie\-rung~--~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<72>gung ziehen.
2001-07-02 12:52:18 +00:00
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.
2004-12-02 13:54:06 +00:00
\section{HowTo}
2001-07-02 12:52:18 +00:00
2004-12-02 13:54:06 +00:00
Zun<EFBFBD>chst soll die Frage gekl<6B>rt werden, wie man <20>berhaupt ein ausf<73>hrbares
Shell-Skript schreibt. Dabei wird vorausgesetzt, da<64> 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.
2001-07-02 12:52:18 +00:00
2004-12-02 13:54:06 +00:00
Zun<EFBFBD>chst mu<6D> mit Hilfe des Editors eine Textdatei angelegt werden, in die der
`Quelltext' geschrieben wird. Dabei mu<6D> darauf geachtet werden, da<64> sich keine
CR/LF-Zei\-len\-um\-br<EFBFBD>\-che einschleichen, wie dies leicht bei der Benutzung
von MS-DOS bzw. Windows-Systemen zur Bearbeitung von Skripten <20>ber das Netzwerk
passieren kann. Wie der Quelltext 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.
2001-07-02 12:52:18 +00:00
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<EFBFBD>hrt, sondern das Systemkommando. Dies ist einer der h<>ufigsten und
zugleich einer der verwirrendsten Anf<6E>ngerfehler. Mehr zu dem
\texttt{test}-Kommando unter \ref{bedingungen}.} mu<6D> die sie ausf<73>hrbar gemacht
werden. Das geht mit dem Unix-Kommando
2004-12-02 13:54:06 +00:00
\texttt{chmod}\index{chmod=\texttt{chmod}} und wird in Abschnitt \ref{chmod}
ausf<EFBFBD>hrlich beschrieben. An dieser Stelle reicht uns ein Aufruf in der Form
2004-12-10 14:38:03 +00:00
\lstinline/chmod 755 name/, um das Skript f<>r alle Benutzer ausf<73>hrbar zu
2004-12-02 13:54:06 +00:00
machen.
Dann kann das Skript gestartet werden. Da sich aus Sicherheitsgr<67>nden auf den
meisten Systemen das aktuelle Verzeichnis nicht im Pfad des Benutzers befindet,
2004-12-10 14:38:03 +00:00
mu<EFBFBD> man der Shell noch mitteilen, wo sie zu suchen hat: Mit \lstinline|./name|
wird versucht, im aktuellen Verzeichnis (\lstinline|./|) ein Programm namens
\lstinline|name| auszuf<75>hren.
2001-07-02 12:52:18 +00:00
2004-11-19 12:09:34 +00:00
Auf den meisten Systemen befindet sich im Pfad ein Verweis auf das Verzeichnis
\texttt{bin} unterhalb des Home-Verzeichnisses eines Benutzers. Das bedeutet
da<EFBFBD> man Skripte die immer wieder benutzt werden sollen dort ablegen kann, so
da<EFBFBD> sie auch ohne eine Pfadangabe gefunden werden. Wie der Pfad genau aussieht
2004-12-10 14:38:03 +00:00
kann man an der Shell durch Eingabe von \lstinline/echo $PATH/\index{\$PATH=\texttt{\$PATH}} herausfinden.
2005-01-21 17:23:30 +00:00
\section{Fehlersuche}\label{fehlersuche}\index{Fehlersuche|(}\index{debuggen|(}
2004-12-10 14:38:03 +00:00
Es gibt f<>r Shell-Skripte keine wirklichen Debugger, aber trotzdem verf<72>gt man
<EFBFBD>ber einige bew<65>hrte Methoden zum Aufsp<73>ren von Fehlern:
\begin{itemize}
\item Debug-Ausgaben: Das wohl einfachste Mittel um herauszufinden was im
Skript vor sich geht sind wohl regelm<6C><6D>ige Debug-Ausgaben. Dazu f<>gt man
einfach an `strategisch wichtigen' Punkten im Skript \texttt{echo}-Zeilen ein,
die Auskunft <20>ber den Status geben.
\item Syntax-Check: Wenn man das Skript in der Form
\lstinline|sh -n ./skriptname| aufruft, wird es nicht wirklich ausgef<65>hrt.
Lediglich die Syntax der Kommandos wird gepr<70>ft. Diese Methode findet nat<61>rlich
keine logischen Fehler, und selbst wenn dieser Aufruf ohne Probleme durchl<68>uft
kann sich zur Laufzeit noch ein anderer Fehler einschleichen.
\item \texttt{set -x}: Wenn in einem Skript der Aufruf \lstinline|set -x|
abgesetzt wird, gibt die Shell jede Zeile nach der Expandierung aber vor der
Ausf<EFBFBD>hrung aus. Dadurch ist klar ersichtlich wann welche Kommandos mit welchen
Parametern ausgef<65>hrt werden. Um den Effekt wieder aufzuheben benutzt man
\lstinline|set +x|. Man kann die Option auch auf das komplette Skript anwenden
ohne sie in das Skript einbauen zu m<>ssen. Dazu startet man das Skript nicht
einfach durch \lstinline|./skriptname| sondern durch
\lstinline|sh -x ./skriptname|.
\item \texttt{set -v}: Dies funktioniert genau wie \lstinline|set -x|, auch der
Aufruf von der Kommandozeile <20>ber \lstinline|sh -v ./skriptname| funktioniert.
Diese Option gibt jede Zeile vor der Ausf<73>hrung aus, allerdings im Gegensatz zu
\texttt{-x} nicht in der expandierten sondern in der vollen Form.
2004-12-10 14:38:03 +00:00
2005-01-21 17:23:30 +00:00
\item \texttt{set -e}: Alle g<>ngigen Shell-Kommandos liefern einen
R<EFBFBD>ckgabewert, der Auskunft <20>ber Erfolg oder Mi<4D>erfolg gibt (siehe Abschnitt
\ref{exitcode}). Normalerweise liegt es beim Programmierer, diese Werte zu
interpretieren. Setzt man aber mit dem Schalter \texttt{-e} den sogenannten
errexit-Modus, beendet die Shell das Skript sobald ein Kommando sich mit einem
R<EFBFBD>ckgabewert ungleich 0 beendet.
Ausnahmen gibt es lediglich, wenn das betroffene Kommando in ein Konstrukt
wie \texttt{while}, \texttt{until} oder \texttt{if} eingebunden ist. Auch wenn
der R<>ckgabewert mittels \texttt{\&\&} oder \texttt{||} verarbeitet wird,
beendet sich die Shell nicht.
2004-12-10 14:38:03 +00:00
\item System-Log: F<>r das direkte Debuggen ist dieser Weg weniger geeignet,
aber gerade in unbeobachtet laufenden Skripten sollte man unerwartete Zust<73>nde
oder besondere Ereignisse im System-Log festhalten. Dies geschieht mit dem
Kommando \texttt{logger}, das in Abschnitt \ref{logger} beschrieben wird.
\item \texttt{script}: Mit dem Kommando \texttt{script} kann eine Sitzung an
der Shell vollst<73>ndig protokolliert werden, inclusive aller Ein- und Ausgaben.
Das umfa<66>t sogar Dr<44>cke auf die Pfeiltasten oder auf Backspace. So kann auch
eine l<>ngere Sitzung mit vielen Ein- und Ausgaben nach dem Testlauf in aller
Ruhe analysiert werden. Das Kommando wird in Abschnitt \ref{script}
beschrieben.
2005-01-21 17:23:30 +00:00
\item \texttt{tee}: Wenn Ausgaben eines Kommandos durch den Filter \texttt{tee}
geschoben werden, k<>nnen sie in einer Datei mitgeschrieben werden. Auch diese
Variante bietet einen stre<72>freien Blick auf unter Umst<73>nden sehr lange und
komplexe Ausgaben. Abschnitt \ref{tee} gibt weitere Hinweise zu dem Kommando.
\item Variablen `tracen': Das Kommando \texttt{trap} (Abschnitt \ref{trap})
reagiert auf Signale. Die Shell erzeugt nach jedem Kommando das Signal DEBUG,
so da<64> mit dem folgenden Kommando daf<61>r gesorgt werden kann, da<64> der Inhalt
einer Variablen nach jedem Kommando ausgegeben wird:
\lstinline|trap 'echo "Trace> \$var = \"$var\""' DEBUG|
2004-12-10 14:38:03 +00:00
\end{itemize}
2001-07-02 12:52:18 +00:00
2005-01-21 17:23:30 +00:00
\index{Fehlersuche|)}\index{debuggen|)}
2001-07-02 12:52:18 +00:00
2004-12-02 13:54:06 +00:00
\section{R<EFBFBD>ckgabewerte}\label{exitcode}\index{R<EFBFBD>ckgabewert|(textbf}\index{Exit-Code|see{R<EFBFBD>ckgabewert}}\index{Exit-Status|see{R<EFBFBD>ckgabewert}}
2001-07-02 12:52:18 +00:00
2004-11-12 12:07:32 +00:00
Wenn unter Unix ein Proze<7A> beendet wird, gibt er einen R<>ckgabewert (auch
Exit-Code oder Exit-Status genannt) an seinen aufrufenden Proze<7A> zur<75>ck. So
kann der Mutterproze<7A> kontrollieren, ob die Ausf<73>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.
2001-07-02 12:52:18 +00:00
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<63>tzbarem Wert. So kann
das Skript automatisch entscheiden, ob bestimmte Aktionen ausgef<65>hrt werden
sollen, die von anderen Aktionen ab\-h<EFBFBD>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 <20>ber Befehlsformen (\ref{befehlsformen}).
2001-07-02 12:52:18 +00:00
2004-11-12 12:07:32 +00:00
In der Bourne-Shell wird der Exit-Code des letzten aufgerufenen Programms in
der Variable \texttt{\$?}\index{\$?=\texttt{\$?}} abgelegt. <20>blicherweise geben
Programme den Wert 0 zur<75>ck, bei irgendwelchen Problemen einen von 0
verschiedenen Wert. Das wird im folgenden Beispiel deutlich:
\begin{lstlisting}
$ cp datei /tmp
$ echo $?
0
$ cp datie /tmp
cp: datie: Datei oder Verzeichnis nicht gefunden
$ echo $?
1
\end{lstlisting}
2004-11-12 12:07:32 +00:00
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
2004-12-10 14:38:03 +00:00
der Ausdruck erfolgreich war~--~gel<65>scht wird:
2004-11-12 12:07:32 +00:00
\lstinline/$ lpr datei && rm datei/\index{\&\&=\texttt{\&\&}}
2001-07-02 12:52:18 +00:00
2004-11-12 12:07:32 +00:00
N<EFBFBD>heres zur Verkn<6B>pfung von Aufrufen steht im Kapitel <20>ber Befehlsformen
(\ref{befehlsformen}). Beispiele zur Benutzung von R<>ckgabewerten in Schleifen
finden sich im Anhang unter \ref{beisp_schleifen_exitcode}.
2001-07-02 12:52:18 +00:00
2004-11-12 12:07:32 +00:00
Auch Shell-Skripte k<>nnen einen R<>ckgabewert an aufrufende Prozesse
zur<EFBFBD>ckgeben. Wie das geht, steht in dem Abschnitt zu \texttt{exit}
(\ref{exit}).
2001-07-02 12:52:18 +00:00
2004-11-12 12:07:32 +00:00
\index{R<EFBFBD>ckgabewert|)}
2001-07-02 12:52:18 +00:00
2004-11-12 12:07:32 +00:00
\section{Variablen}\index{Variablen|(textbf}
2001-07-02 12:52:18 +00:00
2004-11-12 12:07:32 +00:00
In einem Shell-Skript hat man~--~genau wie bei der interaktiven Nutzung der
Shell~--~M<>glichkeiten, <20>ber Variablen zu verf<72>gen. Anders als in den meisten
modernen Programmiersprachen gibt es aber keine Datentypen\index{Datentypen}
wie Ganzzahlen, Flie<69>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
<EFBFBD>bernehmen soll, dann mu<6D> das verarbeitende Programm die Variable entsprechend
interpretieren\footnote{F<EFBFBD>r arithmetische Operationen steht das Programm
\texttt{expr}\index{expr=\texttt{expr}} zur Verf<72>gung (siehe
Z<EFBFBD>hlschleifen-Beispiel unter \ref{while})}.
Man mu<6D> 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<64>tzlich
werden Variablen w<>hrend der Ausf<73>hrung des Skriptes immer an den Stellen
ersetzt, an denen sie stehen. Das passiert in jeder Zeile, unmittelbar bevor
sie ausgef<65>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<75>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<6E>ngt wird von der Shell als Variable angesehen und entsprechend
behandelt (expandiert).
2001-07-02 12:52:18 +00:00
\index{Variablen|)}
\section{Vordefinierte Variablen}\label{vordefinierte_variablen}\index{Variablen}\index{vordefinierte Variablen}
2004-11-19 12:09:34 +00:00
\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}
2004-11-26 15:40:47 +00:00
\index{\$PATH=\texttt{\$PATH}|(textbf}
2004-11-19 12:09:34 +00:00
\index{\$PWD=\texttt{\$PWD}|(textbf}
\index{\$OLDPWD=\texttt{\$OLDPWD}|(textbf}
\index{ERRNO=\texttt{ERRNO}|see{\$ERRNO}}
\index{IFS=\texttt{IFS}|see{\$IFS}}
2004-11-26 15:40:47 +00:00
\index{PATH=\texttt{PATH}|see{\$PATH}}
2004-11-19 12:09:34 +00:00
\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.
2001-07-02 12:52:18 +00:00
Die wichtigsten eingebauten Shell-Variablen sind:\nopagebreak
\LTXtable{\textwidth}{tab_vordefinierte_variablen.tex}
2004-11-19 12:09:34 +00:00
Die Variable \texttt{\$IFS} enth<74>lt per Default die Blank-Zeichen, also
Newline, Space und Tab. Man kann sie aber auch mit anderen Zeichen
<EFBFBD>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}|)}
2004-11-26 15:40:47 +00:00
\index{\$PATH=\texttt{\$PATH}|)}
2004-11-19 12:09:34 +00:00
\index{\$PWD=\texttt{\$PWD}|)}
\index{\$OLDPWD=\texttt{\$OLDPWD}|)}
2001-07-02 12:52:18 +00:00
\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<66>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
<EFBFBD>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 <20>ber die Mustererkennung (\ref{mustererkennung}).
2001-07-02 12:52:18 +00:00
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
\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<EFBFBD>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<EFBFBD>hrungszeichen}}\index{\textbackslash=\texttt{\textbackslash}|see{Backslash}}
Dies ist ein sehr schwieriges Thema, da hier mehrere <20>hnlich aussehende Zeichen
v<EFBFBD>llig verschiedene Effekte bewirken. Die Bourne-Shell unterscheidet allein
zwischen drei verschiedenen Anf<6E>hrungszeichen. Das Quoten dient dazu, bestimmte
Zeichen mit einer Sonderbedeutung vor der Shell zu `verstecken' um zu
verhindern, da<64> diese expandiert (ersetzt) werden.
2001-07-02 12:52:18 +00:00
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
\begin{lstlisting}
$ echo 'Ticks "sch<63>tzen" Anf<6E>hrungszeichen'
Ticks "sch<63>tzen" Anf<6E>hrungszeichen
$ echo "Ist dies ein \"Sonderfall\"?"
Ist dies ein "Sonderfall"?
$ echo "Sie haben `ls | wc -l` Dateien in `pwd`"
Sie haben 43 Dateien in /home/rschaten
$ echo "Der Wert von \$x ist $x"
Der Wert von $x ist 100
\end{lstlisting}
2001-07-02 12:52:18 +00:00
\index{Anf<EFBFBD>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|)}
2005-01-21 17:23:30 +00:00
\section{Meta-Zeichen}\index{Meta-Zeichen|(textbf}\index{Wildcards|see{Metazeichen}}\index{Joker-Zeichen|see{Metazeichen}}\index{Platzhalter|see{Metazeichen}}\index{Globbing|see{Metazeichen}}\label{metazeichen}
2001-07-02 12:52:18 +00:00
\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}
2005-01-21 17:23:30 +00:00
Bei der Angabe von Dateinamen k<>nnen eine Reihe von
Meta-Zeichen\footnote{Meta-Zeichen werden auch Wildcards, Joker-Zeichen oder
Platzhalter genannt. Meint man die Expansion der Meta-Zeichen zu Dateinamen ist
auch von `Globbing' die Rede.} verwendet werden, um mehrere Dateien
gleichzeitig anzusprechen oder um nicht den vollen Dateinamen ausschreiben zu
m<EFBFBD>ssen.
2001-07-02 12:52:18 +00:00
Die wichtigsten Meta-Zeichen sind:\nopagebreak
\LTXtable{\textwidth}{tab_metazeichen.tex}
\texttt{\~}, \texttt{\~{}}\textsl{name}, \texttt{\~{}+} und \texttt{\~{}-}
werden nicht von jeder Shell unterst<73>tzt.
2001-07-02 12:52:18 +00:00
\medskip\emph{Beispiele:}\nopagebreak
\begin{lstlisting}
# Alle Dateien listen, die mit 'neu' anfangen:
$ ls neu*
# 'neuX', 'neu4', aber nicht 'neu10' listen:
$ ls neu?
# Alle Dateien listen, die mit einem Grossbuchstaben zwischen D und R
# anfangen - Natuerlich ist die Shell auch hier Case-Sensitive:
$ ls [D-R]*
\end{lstlisting}
Hier ist anzumerken, da<64> Hidden Files (Dateien, deren Name mit einem Punkt
beginnt) nicht durch ein einfaches \texttt{*} erfa<66>t werden, sondern nur durch
das Suchmuster \texttt{.*}.
2001-07-02 12:52:18 +00:00
\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<EFBFBD>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
2005-01-21 17:23:30 +00:00
eingesetzt werden und den Meta-Zeichen, die in mehreren Programmen Verwendung
finden, um z. B. Suchmuster zu definieren. Diese Muster werden auch regul<75>re
Ausdr<EFBFBD>cke (regular expression)\index{Regular Expression|see{Regul<EFBFBD>rer
Ausdruck}}\index{Expression|see{Regul<EFBFBD>rer
2001-07-02 12:52:18 +00:00
Ausdruck}}\index{Ausdruck|see{Regul<EFBFBD>rer Ausdruck}} genannt. Sie bieten
wesentlich mehr M<>glichkeiten als die relativ einfachen Wildcards f<>r
Dateinamen.
2004-12-02 13:54:06 +00:00
In der folgenden Tabelle wird gezeigt, in welchen Unix-Tools welche Zeichen zur
Ver\-f<EFBFBD>\-gung stehen. Eine ausf<73>hrlichere Beschreibung der Eintr<74>ge findet sich
auf Seite \pageref{beschreibung_der_muster}. \nopagebreak
2001-07-02 12:52:18 +00:00
\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<EFBFBD>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|)}
2001-09-25 17:48:00 +00:00
\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<72>gung.
<EFBFBD>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<65>st. Die Ergebnisse
werden nicht sortiert, sondern in der Reihenfolge ihrer Erstellung
zur<EFBFBD>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
\begin{lstlisting}
# Folgende Verzeichnisse erzeugen:
# - /usr/local/src/bash/old
# - /usr/local/src/bash/new
# - /usr/local/src/bash/dist
# - /usr/local/src/bash/bugs
$ mkdir /usr/local/src/bash/{old,new,dist,bugs}
# Die folgenden Dateien / Verzeichnisse dem Benutzer root zuweisen:
# - /usr/ucb/ex
# - /usr/ucb/edit
# - /usr/lib/ex?.?*
# - /usr/lib/how_ex
# Dabei wird /usr/lib/ex?.?* noch weiter expandiert.
$ chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}
\end{lstlisting}
2001-09-25 17:48:00 +00:00
\index{Klammer-Expansion|)}
2004-11-05 16:20:53 +00:00
\section{Arithmetik-Expansion\label{arithmetikexpansion}\index{Arithmetik-Expansion|(textbf}}
Auch hier werden Klammern expandiert. Allerdings gleich doppelte Klammern. Mit
2004-12-10 14:38:03 +00:00
einem Konstrukt in der Form \lstinline|i=$(($i + 1))| k<>nnen einfache
2004-11-05 16:20:53 +00:00
Berechnungen angestellt werden.
Dabei wird der Ausdruck in den Klammern bewertet als ob er in doppelten
2004-11-19 12:09:34 +00:00
An\-f<EFBFBD>h\-rungs\-zei\-chen stehen w<>rde. Das bedeutet zum Einen, da<64> man auch
mit Variablen rechnen kann, zum anderen macht es das Quoten des Sternchens
2004-11-05 16:20:53 +00:00
<EFBFBD>berfl<EFBFBD>ssig.
F<EFBFBD>r komplexere Berechnungen steht das Tool \texttt{bc} (Siehe Abschnitt
2004-11-19 12:09:34 +00:00
\ref{bc}) zur Ver\-f<EFBFBD>\-gung.
2004-11-05 16:20:53 +00:00
\index{Arithmetik-Expansion|)}
2004-12-02 13:54:06 +00:00
\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.
2004-12-10 14:38:03 +00:00
Diese Zusammenh<6E>nge lassen sich sehr sch<63>n durch die Ausgabe eines Kommandos
wie \texttt{pstree} oder \lstinline|ps -efH| darstellen, letzteres zeigt auch
gleich die PIDs und die PPIDs an.
2004-12-02 13:54:06 +00:00
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.
\index{Prozess|)}\index{Subshell|)}
2001-07-02 12:52:18 +00:00
\section{Programmablaufkontrolle}
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
\subsection{Kommentare\label{kommentare}\index{Kommentar|(textbf} (\texttt{\#})}\index{\#=\texttt{\#}|see{Kommentar}}
2004-11-12 12:07:32 +00:00
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}).
2001-07-02 12:52:18 +00:00
\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<65>hrt werden soll. Das System <20>ffnet dann eine
Subshell\index{Subshell} und f<>hrt das restliche Skript in dieser aus.
2004-12-10 14:38:03 +00:00
Die Angabe erfolgt <20>ber eine Zeile in der Form \lstinline|#!/bin/sh|, wobei
unter \lstinline|/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 wurde mit dem Aufkommen modernerer Shells eingef<65>hrt um eben
durch die Angabe von \lstinline|#!/bin/sh| die Bourne-Shell f<>r die Ausf<73>hrung
von Shell-Skripten benutzen zu k<>nnen. Interpretiert wird die Zeile vom Kernel,
in der Shell selbst wirkt das f<>hrende \verb\#\ als Kommentarzeichen.
2001-07-02 12:52:18 +00:00
\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<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<EFBFBD>glich zu machen.
2001-07-02 12:52:18 +00:00
2005-01-21 17:23:30 +00:00
\begin{lstlisting}
if who | grep $1 > /dev/null; then # who: Liste der Benutzer
# grep: Suche nach Muster
: # tut nichts
else
echo "Benutzer $1 ist nicht angemeldet"
fi
\end{lstlisting}
2001-07-02 12:52:18 +00:00
\subsection{Source (\texttt{.})}\label{source}\index{source=\texttt{source}|(textbf}\index{.=\texttt{.}|see{source}}
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.
2004-12-10 14:38:03 +00:00
Wenn es aber doch gew<65>nscht wird, da<64> ein Skript die Umgebung des Benutzers
<EFBFBD>ndern kann, dann mu<6D> es mit dem Source-Kommando aufgerufen werden. Das wird in
der Form \lstinline|source skriptname| bzw. \lstinline|. skriptname| angegeben.
2005-02-03 22:24:18 +00:00
Er wirkt <20>hnlich wie ein \lstinline|#include| in der Programmiersprache C.
2004-12-10 14:38:03 +00:00
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
2005-02-03 22:24:18 +00:00
den Konfigurationsdateien des Benutzers (z. B. \lstinline|.profile|,
\lstinline|.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<EFBFBD>nnen (siehe Beispiel unter~\ref{init-skript}).
2001-07-02 12:52:18 +00:00
\index{source=\texttt{source}|)}
\subsection{Funktionen}\label{funktionen}\index{Funktion|(textbf}
2001-07-02 12:52:18 +00:00
2004-12-10 14:38:03 +00:00
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 (\texttt{sh}) nicht <20>ber Aliase\index{Aliase} verf<72>gt, k<>nnen
einfache Funktionen als Ersatz dienen.
2001-07-02 12:52:18 +00:00
Der R<>ckgabewert einer Funktion ist gleich dem R<>ckgabewert des letzten in der
Funktion aufgerufenen Kommandos, es sei denn man gibt mittels
2004-12-10 14:38:03 +00:00
\texttt{return} (Siehe \ref{return}) explizit einen anderen Wert zur<75>ck.
2001-07-02 12:52:18 +00:00
\medskip\emph{Beispiel:} Die Funktion gibt die Anzahl der Dateien im aktuellen
Verzeichnis aus. Aufgerufen wird diese Funktion wie ein Befehl, also einfach
2004-12-10 14:38:03 +00:00
durch die Eingabe von \texttt{count}.
\begin{lstlisting}
count () {
ls -1 | wc -l # ls: Liste aller Dateien im Verzeichnis
# Mit Parameter -1 einspaltig
# wc: Word-Count, z<>hlt mit Parameter -l Zeilen
}
\end{lstlisting}
2004-12-10 14:38:03 +00:00
2001-07-02 12:52:18 +00:00
\index{Funktion|)}
\subsection{Bedingungen (\texttt{[ ]})}\label{bedingungen}\index{Bedingungen|see{test}}\index{[ ]=\texttt{[ ]}|see{test}}\index{test=\texttt{test}|(textbf}
Urspr<EFBFBD>nglich konnte die Standard-Shell keine arithmetischen oder logischen
Ausdr<EFBFBD>cke auswerten\footnote{\texttt{if} und Konsorten pr<70>fen nur den
2004-11-12 12:07:32 +00:00
R<EFBFBD>ckgabewert\index{R<EFBFBD>ckgabewert} eines aufgerufenen Programmes~--~0 bedeutet
`true', alles andere bedeutet `false', siehe auch \ref{exitcode}.}. F<>r diese
Aufgabe mu<6D>te ein externes Programm benutzt werden, heutzutage ist der Befehl
in die Shell integriert.
Dieser Befehl hei<65>t \verb\test\\index{test=\texttt{test}}. <20>blicherweise
steht er auf allen Systemen auch noch unter dem Namen \verb\[\ zur Verf<72>gung.
Diese Variante ist fast absolut gleichwertig zu benutzen (in dieser Form wird
allerdings eine abschlie<69>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.
<EFBFBD>. lesbarer zu machen.
\verb\test\ bietet sehr umfangreiche Optionen an. Dazu geh<65>ren Dateitests und
Vergleiche von Zeichenfolgen oder ganzen Zahlen. Diese Bedingungen k<>nnen auch
durch Verkn<6B>pfungen kombiniert werden.
2001-07-02 12:52:18 +00:00
\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<62>ngig.
2001-07-02 12:52:18 +00:00
Die Syntax der \texttt{if}-Anweisung lautet wie folgt:\nopagebreak
\begin{sybox}
\texttt{if }\textsl{Bedingung1} \\
\texttt{then }\textsl{Befehle1} \\
\textsl{[}\texttt{ elif }\textsl{Bedingung2} \\
\hspace*{1em}\texttt{then }\textsl{Befehle2 ]} \\
\hspace*{1em}\texttt{\vdots} \\
\textsl{[}\texttt{ else }\textsl{Befehle3 ]} \\
\texttt{fi}
\end{sybox}
Wenn die \textsl{Bedingung1} erf<72>llt ist, werden die \textsl{Befehle1}
ausgef<EFBFBD>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.
2001-07-02 12:52:18 +00:00
\medskip\emph{Beispiele:} Man achte auf die Positionierung der Semikola\footnote{Und man verzeihe mir einen eventuell falschen Plural\ldots :-)}.\nopagebreak
\begin{lstlisting}
# Fuege eine 0 vor Zahlen kleiner 10 ein:
if [ $counter -lt 10 ]; then
number=0$counter
else
number=$counter;
fi
# Loesche ein Verzeichnis, wenn es existiert:
if [ -d $dir ]; then
rmdir $dir # rmdir: Verzeichnis loeschen
fi
\end{lstlisting}
2001-07-02 12:52:18 +00:00
\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, <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
\begin{sybox}
\texttt{case }\textsl{Wert}\texttt{ in} \\
\hspace*{1em}\textsl{Muster1}\texttt{) }\textsl{Befehle1}\texttt{;;} \\
\hspace*{1em}\textsl{Muster2}\texttt{) }\textsl{Befehle2}\texttt{;;} \\
\hspace*{1em}\texttt{\vdots} \\
\texttt{esac} \\
\end{sybox}
Wenn der \textsl{Wert} mit dem \textsl{Muster1} <20>bereinstimmt, wird die
entsprechende Befehlsfolge\index{Befehls>-folge} (\textsl{Befehle1})
ausgef<EFBFBD>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<EFBFBD> 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.
2001-07-02 12:52:18 +00:00
\medskip\emph{Beispiele:}\nopagebreak
\begin{lstlisting}
# Mit dem ersten Argument in der Befehlszeile wird die entsprechende
# Aktion festgelegt:
case $1 in # nimmt das erste Argument
Ja|Nein) response=1;;
-[tT]) table=TRUE;;
*) echo "Unbekannte Option"; exit 1;;
esac
# Lies die Zeilen von der Standard-Eingabe, bis eine Zeile mit einem
# einzelnen Punkt eingegeben wird:
while :; do # Null-Befehl (immer wahr)
echo "Zum Beenden . eingeben ==> \c"
read line # read: Zeile von StdIn einlesen
case "$line" in
.) echo "Ausgefuehrt"
break;;
*) echo "$line";;
esac
done
\end{lstlisting}
2001-07-02 12:52:18 +00:00
\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}}
2004-11-12 12:07:32 +00:00
Dieses Konstrukt <20>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 <20>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<6D> man dazu die
\texttt{while}-Schleife\index{while=\texttt{while}} `mi<6D>brauchen' (siehe unter
\ref{while}).}.
2001-07-02 12:52:18 +00:00
Die Syntax der \texttt{for}-Schleife lautet wie folgt:\nopagebreak
\begin{sybox}
\texttt{for }\textsl{x [}\texttt{ in }\textsl{Liste ]} \\
\texttt{do} \\
\hspace*{1em}\textsl{Befehle} \\
\texttt{done}
\end{sybox}
2001-07-02 12:52:18 +00:00
2004-11-12 12:07:32 +00:00
Die \textsl{Befehle} werden ausgef<65>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<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.
2001-07-02 12:52:18 +00:00
\medskip\emph{Beispiele:}\nopagebreak
\begin{lstlisting}
# Seitenweises Formatieren der Dateien, die auf der Befehlszeile
# angegeben wurden, und speichern des jeweiligen Ergebnisses:
for file do
pr $file > $file.tmp # pr: Formatiert Textdateien
done
# Durchsuche Kapitel zur Erstellung einer Wortliste (wie fgrep -f):
for item in `cat program_list` # cat: Datei ausgeben
do
echo "Pruefung der Kapitel auf"
echo "Referenzen zum Programm $item ..."
grep -c "$item.[co]" chap* # grep: nach Muster suchen
done
# Ermittle einen Ein-Wort-Titel aus jeder Datei und verwende ihn
# als neuen Dateinamen:
for file do
name=`sed -n 's/NAME: //p' $file`
# sed: Skriptsprache zur Textformatierung
mv $file $name # mv: Datei verschieben bzw. umbenennen
done
\end{lstlisting}
2001-07-02 12:52:18 +00:00
\index{for=\texttt{for}|)}
2002-03-22 15:31:40 +00:00
\subsection{while\ldots}\label{while}\index{while=\texttt{while}|(textbf}\index{Schleife>while-=\texttt{while}-|see{while}}
2001-07-02 12:52:18 +00:00
Die \texttt{while}-Schleife ist wieder ein Konstrukt, das einem aus vielen
anderen Sprachen bekannt ist: die Kopfgesteuerte Schleife.
2001-07-02 12:52:18 +00:00
Die Syntax der \texttt{while}-Schleife lautet wie folgt:\nopagebreak
\begin{sybox}
\texttt{while }\textsl{Bedingung}\texttt{; do} \\
\hspace*{1em}\textsl{Befehle} \\
\texttt{done}
\end{sybox}
Die \textsl{Befehle} werden so lange ausgef<65>hrt, wie die \textsl{Bedingung}
erf<EFBFBD>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.
2001-07-02 12:52:18 +00:00
\medskip\emph{Beispiel:}\nopagebreak
\begin{lstlisting}
# Zeilenweise Ausgabe aller Aufrufparameter:
while [ -n "$1" ]; do
echo $1
shift # mit shift werden die Parameter nach
# Links geshiftet (aus $2 wird $1)
done
\end{lstlisting}
2001-07-02 12:52:18 +00:00
Eine Standard-Anwendung der \texttt{while}-Schleife ist der Ersatz f<>r die
Z<EFBFBD>hlschleife\index{Z<EFBFBD>hlschleife}\index{Schleife>Z<>hl-=Z<>hl-|see{Z<EFBFBD>hlschleife}}.
In anderen Sprachen kann man mit der
\texttt{for}-Schleife\index{for=\texttt{for}} eine
Laufvariable\index{Laufvariable} <20>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
2004-11-05 16:20:53 +00:00
das Kommando \texttt{seq} (Siehe Abschnitt \ref{seq}) zur Verf<72>gung.}, ersetzt
man die Funktion durch geschickte Anwendung der
2001-07-02 12:52:18 +00:00
\texttt{while}-Schleife:\nopagebreak
\begin{lstlisting}
# Ausgabe der Zahlen von 1 bis 100:
i=1
while [ $i -le 100 ]; do
echo $i
i=`expr $i + 1`
done
\end{lstlisting}
Ein weiterer typischer Anwendungsfall ist das zeilenweise Bearbeiten einer
Eingabedatei. Dabei kann es sich entweder um eine einfache Textdatei handeln,
oder um die Ausgabe eines anderen Kommandos.
Um die Ausgabe eines anderen Kommandos zu verarbeiten kann \texttt{while} als
Teil einer Pipeline geschrieben werden:
\begin{lstlisting}
# "hallo" suchen und umstaendlich ausgeben:
grep "hallo" datei.txt | while read zeile; do
echo "Fundstelle: $zeile"
done
\end{lstlisting}
Wenn die Eingabe als Textdatei vorliegt ist es verlockend, diese einfach
mittels \texttt{cat} auszugeben und per Pipe in die Schleife zu schicken.
Allerdings sollte an dieser Stelle eine Umleitung benutzt werden. So vermeidet
man den <20>berfl<66>ssigen Start des Kommandos \texttt{cat}:
\begin{lstlisting}
# Zahlen aus einer Datei lesen und aufsummieren:
summe=0
while read zeile; do
summe=`expr $summe + $zeile`
done < datei.txt
echo "Summe: $summe"
\end{lstlisting}
2001-07-02 12:52:18 +00:00
\index{while=\texttt{while}|)}
2002-03-22 15:31:40 +00:00
\subsection{until\ldots}\label{until}\index{until=\texttt{until}|(textbf}\index{Schleife>until-=\texttt{until}-|see{until}}
2001-07-02 12:52:18 +00:00
2002-03-22 15:31:40 +00:00
Die \texttt{until}-Schleife ist das Gegenst<73>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<64> die Bedingung negiert wird.
Es ist also auch eine kopfgesteuerte Schleife, die allerdings so lange l<>uft
wie die angegebene Bedingung nicht zutrifft.
2001-07-02 12:52:18 +00:00
Die Syntax der \texttt{until}-Schleife lautet wie folgt:\nopagebreak
\begin{sybox}
\texttt{until }\textsl{Bedingung}\texttt{; do} \\
\hspace*{1em}\textsl{Befehle} \\
\texttt{done}
\end{sybox}
2001-07-02 12:52:18 +00:00
2002-03-22 15:31:40 +00:00
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})
2004-11-12 12:07:32 +00:00
formuliert. Wenn die Aus\-f<EFBFBD>h\-rung eines Schleifendurchlaufs bzw der ganzen
2002-03-22 15:31:40 +00:00
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.
2001-07-02 12:52:18 +00:00
\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
\begin{lstlisting}
# Warten, bis sich der Administrator einloggt:
until who | grep "root"; do
# who: Liste der Benutzer
# grep: Suchen nach Muster
sleep 2 # sleep: warten
done
echo "Der Meister ist anwesend"
\end{lstlisting}
2001-07-02 12:52:18 +00:00
\index{until=\texttt{until}|)}
\subsection{continue}\label{continue}\index{continue=\texttt{continue}|(textbf}
Die Syntax der \texttt{continue}-Anweisung lautet wie folgt:\nopagebreak
\begin{sybox}
\texttt{continue }\textsl{[ n ]}
\end{sybox}
Man benutzt \texttt{continue} um die restlichen Befehle in einer Schleife zu
<EFBFBD>berspringen und mit dem n<>chsten Schleifendurchlauf anzufangen. Wenn der
Parameter \textsl{n} angegeben wird, werden \textsl{n} Schleifenebenen
<EFBFBD>bersprungen.
2001-07-02 12:52:18 +00:00
\index{continue=\texttt{continue}|)}
\subsection{break}\label{break}\index{break=\texttt{break}|(textbf}
Die Syntax der \texttt{break}-Anweisung lautet wie folgt:\nopagebreak
\begin{sybox}
\texttt{break }\textsl{[ n ]}
\end{sybox}
Mit \texttt{break} kann man die innerste Ebene (bzw. \textsl{n}
Schleifenebenen) verlassen ohne den Rest der Schleife auszuf<75>hren.
2001-07-02 12:52:18 +00:00
\index{break=\texttt{break}|)}
\subsection{exit}\label{exit}\index{exit=\texttt{exit}|(textbf}
Die Syntax der \texttt{exit}-Anweisung lautet wie folgt:\nopagebreak
\begin{sybox}
\texttt{exit }\textsl{[ n ]}
\end{sybox}
2001-07-02 12:52:18 +00:00
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.
\index{exit=\texttt{exit}|)}
\subsection{return}\label{return}\index{return=\texttt{return}|(textbf}
Die Syntax der \texttt{return}-Anweisung lautet wie folgt:\nopagebreak
\begin{sybox}
\texttt{return }\textsl{[ n ]}
\end{sybox}
Mittels \texttt{return} kann eine Funktion (siehe \ref{funktionen}) einen
bestimmten Wert zur<75>ckgeben. Anderenfalls wird der Exit-Code des letzten in der
Funktion ausgef<65>hrten Befehls zur<75>ckgegeben.
\index{return=\texttt{return}|)}
2001-07-02 12:52:18 +00:00
\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<75>hren. So
kommen Verkettungen, Abh<62>ngigkeiten und Gruppierungen zustande:\nopagebreak
2001-07-02 12:52:18 +00:00
\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<EFBFBD>me}\label{datenstrom}\index{Datenstr<EFBFBD>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<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
\LTXtable{\textwidth}{tab_datenstroeme_kanaele.tex}
Die standardm<64><6D>ige Eingabequelle oder das Ausgabeziel k<>nnen wie folgt ge<67>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<65>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<EFBFBD>rlich k<>nnte man zu diesem Zweck eine eigene Datei einrichten, aber das ist
eigentlich nicht notwendig. Man handelt sich nur <20>rger ein, wenn man das Skript
auf einen anderen Rechner portiert und die Datei vergi<67>t. Abgesehen davon - wo
legt man eine solche Datei sinnvoll ab?
Um diesem <20>rger zu entgehen, sollte man in einer solchen Situation ein
2004-11-19 12:09:34 +00:00
Here-Do\-ku\-ment benutzen.
2001-07-02 12:52:18 +00:00
\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
\begin{lstlisting}
# Ein Here-Dokument: Nach dem << wird ein sogenannter Delimiter
# angegeben. Alle folgenden Zeilen werden an die Standard-Eingabe von
# cat <20>bergeben. Der Text wird durch ein erneutes Vorkommen des
# Delimiters (einzeln und am Zeilenanfang) beendet.
cat << EOF
Dieser Text wird zeilenweise ausgegeben,
bis ein einzelnes EOF kommt.
EOF
\end{lstlisting}
2001-07-02 12:52:18 +00:00
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
\begin{lstlisting}
cdrdao read-toc --datafile - cd.toc
cdparanoia -q -R 1- - | cdrdao write --buffers 64 cd.toc
\end{lstlisting}
2005-01-14 16:27:08 +00:00
\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{Here-Dokument|)textbf}
2001-07-02 12:52:18 +00:00
\index{Datenstr<EFBFBD>me|)}