Diverse Aenderungen.

This commit is contained in:
rschaten 2005-01-14 16:27:08 +00:00
parent 483c60f1db
commit 5fdc537aa9
5 changed files with 282 additions and 28 deletions

View File

@ -2,17 +2,17 @@
\chapter{Quellen}\label{quellen}
\begin{itemize}
\item Bash Reference Manual (\texttt{http://www.gnu.org/manual/bash-2.02/\\bashref.html})
\item Bash Reference Manual (\href{http://www.gnu.org/manual/bash-2.02/bashref.html}{http://www.gnu.org/manual/bash-2.02/bashref.html})
\item Die Man-Page der Bash
\item Die deutschsprachige Shell-Newsgroup (\texttt{de.comp.os.unix.shell})
\item Unix In A Nutshell (\texttt{http://www.oreilly.com/catalog/unixnut3/})
\item Unix Power Tools (\texttt{http://www.oreilly.com/catalog/upt2/})
\item Von DOS nach Linux HOWTO (\texttt{http://www.linuxhaven.de/dlhp/HOWTO/DE-DOS-nach-Linux-HOWTO.html})
\item Die deutschsprachige Shell-Newsgroup (\href{news://de.comp.os.unix.shell}{news://de.comp.os.unix.shell})
\item Unix In A Nutshell (\href{http://www.oreilly.com/catalog/unixnut3/}{http://www.oreilly.com/catalog/unixnut3/})
\item Unix Power Tools (\href{http://www.oreilly.com/catalog/upt2/}{http://www.oreilly.com/catalog/upt2/})
\item Von DOS nach Linux HOWTO (\href{http://www.linuxhaven.de/dlhp/HOWTO/DE-DOS-nach-Linux-HOWTO.html}{http://www.linuxhaven.de/dlhp/HOWTO/DE-DOS-nach-Linux-HOWTO.html})
TODO!!! Andere URL angeben (http://www.tldp.org...)
\item Bash Guide for Beginners (\texttt{http://tldp.org/LDP/Bash-Beginners-Guide/})
\item Advanced Bash-Scripting Guide (\texttt{http://tldp.org/LDP/abs/})
\item Bash Guide for Beginners (\href{http://tldp.org/LDP/Bash-Beginners-Guide/}{http://tldp.org/LDP/Bash-Beginners-Guide/})
\item Advanced Bash-Scripting Guide (\href{http://tldp.org/LDP/abs/}{http://tldp.org/LDP/abs/})
\item ... und eine Menge Skripte, die ich im Laufe der Zeit gelesen habe (das
kann ich nur jedem empfehlen - es ist spannender als es sich anhört...).
\end{itemize}

View File

@ -36,7 +36,7 @@ einzupacken, dies durch eine Pipe auf die andere Seite der Verbindung zu
bringen und dort wieder zu entpacken.
Wenn dem Kommando \texttt{tar} an Stelle eines Dateinamens ein Minus-Zeichen
als Archiv gegeben wird, benutzt es~---~je nach der gewählten Aktion~---~die
als Archiv gegeben wird, benutzt es~--~je nach der gewählten Aktion~--~die
Standard-Ein- bzw. -Ausgabe. Diese kann an ein weiteres \texttt{tar} übergeben
werden um wieder entpackt zu werden.
@ -88,15 +88,112 @@ der Name des aktuellen Verzeichnisses auf dem lokalen System.
\section{Binaries inside}
TODO!!! binaries inside
Software wird meistens in Form von Paketen verteilt. Entweder handelt es sich
dabei um auf das Betriebssystem abgestimmte Installationspakete (rpm, deb, pkg
usw.), gepackte Archive (zip, tgz) oder Installationsprogramme. Unter
Unix-Systemen bietet sich für letztere die Shell als Trägersystem an.
Shell-Skripte sind mit wenigen Einschränkungen plattformunabhängig, sie können
also ohne vorherige Installations- oder Compilier-Arbeiten gestartet werden und
die Umgebung für das zu installierende Programm testen und / oder vorbereiten.
Abgesehen davon können Skripte mit den hier vorgestellten Techniken auch andere
Daten, z. B. Bilder oder Töne, enthalten.
Doch wie werden die~--~üblicherweise binären~--~Pakete auf das Zielsystem
gebracht?
Im Prinzip gibt es dafür zwei unterschiedliche Verfahren:
\subsection{Binäre Here-Dokumente}
\index{Here-Dokument}
TODO!!! binäre Here-Dokumente
Eine Möglichkeit ist es, die binäre Datei in Form eines Here-Dokuments
mitzuliefern. Da es aber in der Natur einer binären Datei liegt nicht-druckbare
Zeichen zu enthalten, kann die Datei mit Hilfe des Tools \texttt{uuencode}
vorbereitet werden. Das Tool codiert Eingabedateien so, daß sie nur noch
einfache Textzeichen enthalten.
Sehen wir uns das folgende einfache Beispiel an. Es ist etwas wild konstruiert
und nicht sehr sinnvoll, aber es zeigt das Prinzip.
\begin{lstlisting}
#!/bin/sh
echo "Das Bild wird ausgepackt..."
uudecode << 'EOF'
begin 644 icon.png
MB5!.1PT*&@H````-24A$4@```!8````6"`8```#$M&P[````"7!(67,```L3
M```+$P$`FIP8````!&=!34$``+&.?/M1DP```"!C2%)-``!Z)0``@(,``/G_
\end{lstlisting}
Nach einem Hinweis wird also das Here-Dokument als Eingabe für das Tool
\texttt{uudecode} benutzt. Erstellt wurde das Dokument mit einer Zeile in der
Form \texttt{uuencode icon.png icon.png}.
Wie man sieht ist der Name der Datei in dem Here-Dokument enthalten. Die Datei
wird entpackt und unter diesem gespeichert. In der `realen Welt' muß an der
Stelle auf jeden Fall sichergestellt werden, daß keine existierenden Dateien
versehentlich überschrieben werden.
Um diesen Abschnitt nicht allzu lang werden zu lassen überspringen wir einen
Teil der Datei.
\begin{lstlisting}[firstnumber=38]
M#-""F4%,@%4.GUZ``"(*`6VW6!S#\>C_?/;__Q<R_S]<P/F7AXDA'I\>@``B
K!>E;2S-,]5!A7`,,U'0@GQ6?8H```P`#@&?)O'P'L0````!)14Y$KD)@@@``
`
end
EOF
if [ $? -ne 0 ]; then
echo "Fehler beim Auspacken der Datei"
exit 1
fi
display icon.png
\end{lstlisting}
Nach dem Entpacken wird noch der Exit-Code von \texttt{uudecode} überprüft und
im Fehlerfall eine Ausgabe gemacht. Im Erfolgsfall wird das Bild mittels
\texttt{display} angezeigt.
\subsection{Schwanz ab!}
TODO!!! Schwanz ab
Diese Variante basiert darauf, daß die binäre Datei ohne weitere Codierung an
das Shell-Skript angehängt wurde. Nachteil dieses Verfahrens ist, daß das
`abschneidende Kommando' nach jeder Änderung der Länge des Skriptes angepaßt
werden muß.
Dabei gibt es zwei Methoden, die angehängte Datei wieder abzuschneiden. Die
einfachere Methode funktioniert mit \texttt{tail}:
\texttt{tail -n +227 \$0 > icon.png}
Dieses Beispiel geht davon aus, daß das Skript selbst 227 Zeilen umfaßt. Die
binäre Datei wurde mit einem Kommando wie \texttt{cat icon.png >> skript.sh} an
das Skript angehängt.
Für die etwas kompliziertere Variante muß die Länge des eigentlichen
Skript-Teiles genau angepaßt werden. Wenn das Skript beispielsweise etwa 5,5kB
lang ist, müssen genau passend viele Leerzeilen oder Kommentarzeichen angehängt
werden, damit sich eine Länge von 6kB ergibt. Dann kann das Anhängsel mit dem
Kommando \texttt{dd} in der folgenden Form abgeschnitten werden:
\texttt{dd bs=1024 if=\$0 of=icon.png skip=6}
Das Kommando kopiert Daten aus einer Eingabe- in eine Ausgabedatei. Im
einzelnen wird hier eine Blockgröße (blocksize, bs) von 1024 Bytes festgelegt.
Dann werden Eingabe- und Ausgabedatei benannt, dabei wird als Eingabedatei
\texttt{\$0} und somit der Name des laufenden Skriptes benutzt. Schließlich
wird festgelegt, daß bei der Aktion die ersten sechs Block~--~also die ersten
sechs Kilobytes~--~übersprungen werden sollen.
Um es nochmal zu betonen: Diese beiden Methoden sind mit Vorsicht zu genießen.
Bei der ersten führt jede zusätzliche oder gelöschte Zeile zu einer kaputten
Ausgabedatei, bei der zweiten reichen schon einzelne Zeilen. In jedem Fall
sollte nach dem Auspacken noch einmal mittels \texttt{sum} oder \texttt{md5sum}
eine Checksumme gezogen und verglichen werden.
\section{Dateien, die es nicht gibt}
@ -165,7 +262,97 @@ Standardeingabe der Schleife (und somit auf das \texttt{read}-Kommando) legen.
\subsection{Daten aus einer Subshell hochreichen}\label{daten_hochreichen}
TODO!!! Daten aus einer Subshell hochreichen
Ein immer wieder auftretendes und oft sehr verwirrendes Problem ist, daß
Variablen die in einer Subshell definiert wurden außerhalb dieser nicht
sichtbar sind (siehe Abschnitt \ref{subshellschleifen}). Dies ist um so
ärgerlicher, als daß Subshells auch bei vergleichsweise einfachen Pipelines
geöffnet werden.
Ein Beispiel für ein mißlingendes Skriptfragment wäre das folgende:
\begin{lstlisting}
nCounter=0
cat datei.txt | while read VAR; do
nCounter=`expr $nCounter + 1`
done
echo "nCounter=$nCounter"
\end{lstlisting}
Die Variable nCounter wird mit 0 initialisiert. Dann wird eine Datei per Pipe
in eine \texttt{while}-Schleife geleitet. Innerhalb der Schleife wird für jede
eingehende Zeile die Variable hochgezählt. Am Ende der Schleife enthält die
Variable tatsächlich den korrekten Wert, aber da die Pipe eine Subshell
geöffnet hat ist der Wert nach Beendigung der Schleife nicht mehr sichtbar. Das
\texttt{echo}-Kommando gibt die Zahl 0 aus.
Es gibt mehrere Ansätze, diesem Problem zu begegnen. Am einfachsten wäre es in
diesem Fall, dem Rat aus Abschnitt \ref{subshellschleifen} zu folgen und die
Subshell geschickt zu vermeiden. Doch das ist leider nicht immer möglich. Wie
geht man in solchen Fällen vor?
Bei einfachen Zahlenwerten könnte beispielsweise ein Rückgabewert helfen.
Komplexere Informationen können in eine temporäre Datei geschrieben werden, die
danach geparst werden müßte. Wenn die Informationen in Zeilen der Form
`VARIABLE=\dq{}Wert\dq{}' gespeichert werden, kann die Datei einfach mittels
\texttt{source} (Abschnitt \ref{source}) oder einem Konstrukt der Art
\texttt{eval `cat tempfile`} gelesen werden.
Und genau mit dieser Überlegung kommen wir zu einem eleganten~--~wenn auch
nicht ganz einfachen~--~Trick.
Anstatt die Daten in eine temporäre Datei zu schreiben, wo sie womöglich durch
andere Prozesse verändert oder ausgelesen werden könnten, kann man sie auch in
`nicht existente' Dateien schreiben. Das folgende Beispiel demonstriert das
Verfahren:
\begin{lstlisting}
#!/bin/sh -x
TMPNAME="/tmp/`date '+%Y%m%d%H%M%S'`$$.txt"
exec 3> "$TMPNAME"
exec 4< "$TMPNAME"
rm -f "$TMPNAME"
\end{lstlisting}
Bis hierher wurde zunächst eine temporäre Datei angelegt. Die Filehandles 3 und
4 wurden zum Schreiben bzw. Lesen mit dieser Datei verbunden. Daraufhin wurde
die Datei entfernt. Die Filehandles verweisen weiterhin auf die Datei, obwohl
sie im Dateisystem nicht mehr sichtbar ist.
Kommen wir zum nützlichen Teil des Skriptes:
\begin{lstlisting}[firstnumber=6]
nCounter=0
cat datei.txt | ( while read VAR; do
while read VAR; do
nCounter=`expr $nCounter + 1`
done
echo "nCounter=$nCounter"
) >&3
\end{lstlisting}
Hier wurde wieder die Variable nCounter initialisiert und in der Subshell die
Zeilen gezählt wie im ersten Beispiel. Allerdings wurde explizit eine Subshell
um die Schleife gestartet. Dadurch steht die in der Schleife hochgezählte
Variable auch nach Beendigung der Schleife zur Verfügung, allerdings immernoch
nur in der Subshell. Um das zu ändern, wird in Zeile 11 der Wert ausgegeben.
Die Ausgaben der Subshell werden in den oben erstellen Deskriptor umgeleitet.
\begin{lstlisting}[firstnumber=13]
echo "(vor eval) nCounter=$nCounter"
eval `cat <&4`
echo "(nach eval) nCounter=$nCounter"
\end{lstlisting}
Das \texttt{echo}-Kommando in Zeile 13 beweist, daß der Wert von nCounter
tatsächlich außerhalb der Subshell nicht zur Verfügung steht. Zunächst.
In Zeile 14 wird dann die ebenfalls oben schon angesprochene
\texttt{eval}-Zeile benutzt, um die Informationen aus dem Filedeskriptor zu
lesen, die die Schleife dort hinterlassen hat.
Abschließend zeigt die Zeile 15, daß der Transport tatsächlich funktioniert
hat, die Variable nCounter ist mit dem Wert aus der Subshell belegt.
\subsection{Dateien gleichzeitig lesen und schreiben}
@ -199,7 +386,56 @@ grep "wichtig" <&3 > "$FILE"
Allerdings sollte man bei dieser Methode beachten, daß man im Falle eines
Fehlers die Quelldaten verliert, da die Datei ja bereits gelöscht wurde.
\section{Auf der Lauer: Wachhunde}
\section{Auf der Lauer: Wachhunde}\label{wachhunde}\index{Watchdog}
TODO!!! Auf der Lauer: Wachhunde
Es kommt vor, daß man einen Prozeß startet, bei dem man sich nicht sicher sein
kann daß er sich auch in absehbarer Zeit wieder beendet. Beispielsweise kann
der Timeout für einen Netzwerkzugriff deutlich höher liegen als erwünscht, und
wenn der `gegnerische' Dienst nicht antwortet bleibt einem nur zu warten.
Es sei denn, man legt einen geeigneten Wachhund\footnote{Der englische Begriff
`Watchdog' ist in diesem Zusammenhang wahrscheinlich geläufiger...} auf die
Lauer, der im Notfall rettend eingreift. In einem Shell-Skript könnte das wie
folgt aussehen:
\begin{lstlisting}
#!/bin/sh
timeout=5
ping 192.168.0.254 &
cmdpid=$!
\end{lstlisting}
Bis hierher nichts aufregendes. Eine Variable wird mit dem Timeout belegt, also
mit der Anzahl an Sekunden nach denen der zu überwachende Prozeß unterbrochen
werden soll. Dann wird der zu überwachende Prozeß gestartet und mittels \& in
den Hintergrund geschickt. Die Prozeß-ID des Prozesses wird in der Variablen
cmdpid gesichert.
\begin{lstlisting}[firstnumber=5]
(sleep $timeout; kill -9 $cmdpid) &
watchdogpid=$!
\end{lstlisting}
In Zeile 5 findet sich der eigentliche Watchdog. Hier wird eine Subshell
gestartet, in der zunächst der oben eingestellte Timeout abgewartet und dann
der zu überwachende Prozeß getötet wird. Diese Subshell wird ebenfalls mit \&
in den Hintergrund geschickt. Die ID der Subshell wird in der Variablen
watchdogpid gesichert.
\begin{lstlisting}[firstnumber=7]
wait $cmdpid
kill $watchdogpid > /dev/null 2>&1
exit 0
\end{lstlisting}
Dann wird durch ein \texttt{wait}\index{wait} darauf gewartet, daß sich der
überwachte Prozeß beendet. Dabei würde \texttt{wait} bis in alle Ewigkeit
warten, wäre da nicht der Watchdog in der Subshell. Wenn dem die Ausführung zu
lange dauert, sorgt er dafür daß der Prozeß beendet wird.
Kommt der überwachte Prozeß aber rechtzeitig zurück, sorgt \texttt{kill} in
Zeile 8 dafür daß der Wachhund `eingeschläfert' wird.
Auf diese Weise ist sichergestellt, daß der \texttt{ping} auf keinen Fall
länger als fünf Sekunden läuft.

View File

@ -32,9 +32,9 @@
%\DeclareGraphicsRule{.tif}{bmp}{}{} % Grafikformate
\usepackage{tabularx} % für Tabellen über die Seitenbreite
\usepackage{longtable} % für Tabellen über die Seitenbreite
\usepackage{supertabular} % für Tabellen über die Seitenbreite
%\usepackage{tabularx} % für Tabellen über die Seitenbreite
%\usepackage{longtable} % für Tabellen über die Seitenbreite
%\usepackage{supertabular} % für Tabellen über die Seitenbreite
\usepackage{ltxtable} % für Tabellen über die Seitenbreite
\usepackage{makeidx} % Index wird später eingefügt
@ -44,7 +44,7 @@
\usepackage{fancybox} % Kästchen für Tastendarstellung
\usepackage{moreverb} % Für Listings
%\usepackage{moreverb} % Für Listings
\usepackage{listings} % Für Listings
\lstset{
extendedchars=true,
@ -69,7 +69,8 @@
bookmarksnumbered,
linktocpage,
colorlinks,
linkcolor = black
linkcolor = black,
urlcolor = black
]{hyperref}
%\pdfinfo{/CreationDate (D:20000301170300-01'00')} % (D:YYYYMMDDhhmmss)
@ -104,14 +105,14 @@
\thispagestyle{empty} % eine Leerseite
~\vfill
\footnotesize
Copyright \copyright{} 2000-2004 Ronald Schaten (\texttt{ronald@schatenseite.de})\bigskip
Copyright \copyright{} 2000-2005 Ronald Schaten (ronald@schatenseite.de)\bigskip
TODO: Vernünftige Lizenz einsetzen!\bigskip
%\texttt{${}$Id${}$}\bigskip
Die aktuellste Version dieses Dokumentes befindet sich auf
\texttt{http://www.schatenseite.de/}.\bigskip
\href{http://www.schatenseite.de/}{http://www.schatenseite.de/}.\bigskip
Dieses Dokument ist entstanden, weil ich für mich selbst eine kompakte
Übersicht zu diesem Thema haben wollte. Ich beabsichtige nicht, damit in
@ -128,12 +129,12 @@ durch ihre konstruktive Kritik zu Verbesserungen beigetragen haben (in
chronologischer Reihenfolge ihres Eingreifens):
\begin{list}{$\bullet$}{\itemsep=-0.5cm}
\item Jürgen Ilse (\texttt{ilse@asys-h.de})\\
\item Christian Perle (\texttt{christian.perle@tu-clausthal.de})\\
\item Andreas Metzler (\texttt{ametzler@downhill.at.eu.org})\\
\item Johannes Kolb (\texttt{johannes.kolb@web.de})\\
\item Falk Friedrich (\texttt{falk@gmx.de})\\
\item Kai Thöne (\texttt{kai.thoene@gmx.de})\\
\item Jürgen Ilse (ilse@asys-h.de)\\
\item Christian Perle (christian.perle@tu-clausthal.de)\\
\item Andreas Metzler (ametzler@downhill.at.eu.org)\\
\item Johannes Kolb (johannes.kolb@web.de)\\
\item Falk Friedrich (falk@gmx.de)\\
\item Kai Thöne (kai.thoene@gmx.de)\\
\end{list}
Und ich bitte alle Leser, auf eventuelle Fehler zu achten und mich darauf

View File

@ -184,6 +184,7 @@ Verf
\item \texttt{pgrep} (\ref{pgrep}): Bestimmte Prozesse suchen
\item \texttt{pkill} (\ref{pkill}): Bestimmte Prozesse töten
\item \texttt{trap} (\ref{trap}): Auf Signale reagieren
\item \texttt{wait} (\ref{wait}): Auf einen Prozeß warten
\end{itemize}
@ -1632,6 +1633,22 @@ sogar ausschlie
\index{uniq=\texttt{uniq}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{wait}\label{wait}\index{wait=\texttt{wait}|(textbf}
Das Kommando \texttt{wait} wird benutzt um auf die Beendigung eines Prozesses
zu warten. Als Parameter wird eine Prozeß-ID übergeben, \texttt{wait} läuft so
lange bis sich der Prozeß mit der angegebenen ID beendet hat.
Wenn keine Prozeß-ID angegeben wurde, wartet \texttt{wait} auf alle
Kind-Prozesse der aktuellen Shell.
Ein Beispiel für die Benutzung findet sich im Kapitel über Wachhunde (Abschnitt
\ref{wachhunde}).
\index{wait=\texttt{wait}|)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{wc}\label{wc}\index{wc=\texttt{wc}|(textbf}

View File

@ -785,5 +785,5 @@ Gerade der Mechanismus mit dem Piping sollte nicht untersch
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{<=\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}
\index{Datenströme|)}