Diverse Aenderungen.
This commit is contained in:
@@ -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<65>hlten Aktion~---~die
|
||||
als Archiv gegeben wird, benutzt es~--~je nach der gew<65>hlten Aktion~--~die
|
||||
Standard-Ein- bzw. -Ausgabe. Diese kann an ein weiteres \texttt{tar} <20>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<54>gersystem an.
|
||||
Shell-Skripte sind mit wenigen Einschr<68>nkungen plattformunabh<62>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~--~<7E>blicherweise bin<69>ren~--~Pakete auf das Zielsystem
|
||||
gebracht?
|
||||
|
||||
Im Prinzip gibt es daf<61>r zwei unterschiedliche Verfahren:
|
||||
|
||||
\subsection{Bin<EFBFBD>re Here-Dokumente}
|
||||
\index{Here-Dokument}
|
||||
|
||||
TODO!!! bin<69>re Here-Dokumente
|
||||
Eine M<>glichkeit ist es, die bin<69>re Datei in Form eines Here-Dokuments
|
||||
mitzuliefern. Da es aber in der Natur einer bin<69>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<64> 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<6D> an der
|
||||
Stelle auf jeden Fall sichergestellt werden, da<64> keine existierenden Dateien
|
||||
versehentlich <20>berschrieben werden.
|
||||
|
||||
Um diesen Abschnitt nicht allzu lang werden zu lassen <20>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} <20>berpr<70>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<64> die bin<69>re Datei ohne weitere Codierung an
|
||||
das Shell-Skript angeh<65>ngt wurde. Nachteil dieses Verfahrens ist, da<64> das
|
||||
`abschneidende Kommando' nach jeder <20>nderung der L<>nge des Skriptes angepa<70>t
|
||||
werden mu<6D>.
|
||||
|
||||
Dabei gibt es zwei Methoden, die angeh<65>ngte Datei wieder abzuschneiden. Die
|
||||
einfachere Methode funktioniert mit \texttt{tail}:
|
||||
|
||||
\texttt{tail -n +227 \$0 > icon.png}
|
||||
|
||||
Dieses Beispiel geht davon aus, da<64> das Skript selbst 227 Zeilen umfa<66>t. Die
|
||||
bin<EFBFBD>re Datei wurde mit einem Kommando wie \texttt{cat icon.png >> skript.sh} an
|
||||
das Skript angeh<65>ngt.
|
||||
|
||||
F<EFBFBD>r die etwas kompliziertere Variante mu<6D> die L<>nge des eigentlichen
|
||||
Skript-Teiles genau angepa<70>t werden. Wenn das Skript beispielsweise etwa 5,5kB
|
||||
lang ist, m<>ssen genau passend viele Leerzeilen oder Kommentarzeichen angeh<65>ngt
|
||||
werden, damit sich eine L<>nge von 6kB ergibt. Dann kann das Anh<6E>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<67><72>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<69>lich
|
||||
wird festgelegt, da<64> bei der Aktion die ersten sechs Block~--~also die ersten
|
||||
sechs Kilobytes~--~<7E>bersprungen werden sollen.
|
||||
|
||||
Um es nochmal zu betonen: Diese beiden Methoden sind mit Vorsicht zu genie<69>en.
|
||||
Bei der ersten f<>hrt jede zus<75>tzliche oder gel<65>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<64>
|
||||
Variablen die in einer Subshell definiert wurden au<61>erhalb dieser nicht
|
||||
sichtbar sind (siehe Abschnitt \ref{subshellschleifen}). Dies ist um so
|
||||
<EFBFBD>rgerlicher, als da<64> Subshells auch bei vergleichsweise einfachen Pipelines
|
||||
ge<EFBFBD>ffnet werden.
|
||||
|
||||
Ein Beispiel f<>r ein mi<6D>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<65>hlt. Am Ende der Schleife enth<74>lt die
|
||||
Variable tats<74>chlich den korrekten Wert, aber da die Pipe eine Subshell
|
||||
ge<EFBFBD>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<6E>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<6F>re Datei geschrieben werden, die
|
||||
danach geparst werden m<><6D>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 <20>berlegung kommen wir zu einem eleganten~--~wenn auch
|
||||
nicht ganz einfachen~--~Trick.
|
||||
|
||||
Anstatt die Daten in eine tempor<6F>re Datei zu schreiben, wo sie wom<6F>glich durch
|
||||
andere Prozesse ver<65>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<75>chst eine tempor<6F>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<65>hlt wie im ersten Beispiel. Allerdings wurde explizit eine Subshell
|
||||
um die Schleife gestartet. Dadurch steht die in der Schleife hochgez<65>hlte
|
||||
Variable auch nach Beendigung der Schleife zur Verf<72>gung, allerdings immernoch
|
||||
nur in der Subshell. Um das zu <20>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<64> der Wert von nCounter
|
||||
tats<EFBFBD>chlich au<61>erhalb der Subshell nicht zur Verf<72>gung steht. Zun<75>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<EFBFBD>end zeigt die Zeile 15, da<64> der Transport tats<74>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<64> man im Falle eines
|
||||
Fehlers die Quelldaten verliert, da die Datei ja bereits gel<65>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<64> man einen Proze<7A> startet, bei dem man sich nicht sicher sein
|
||||
kann da<64> er sich auch in absehbarer Zeit wieder beendet. Beispielsweise kann
|
||||
der Timeout f<>r einen Netzwerkzugriff deutlich h<>her liegen als erw<72>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<65>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 <20>berwachende Proze<7A> unterbrochen
|
||||
werden soll. Dann wird der zu <20>berwachende Proze<7A> gestartet und mittels \& in
|
||||
den Hintergrund geschickt. Die Proze<7A>-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<75>chst der oben eingestellte Timeout abgewartet und dann
|
||||
der zu <20>berwachende Proze<7A> get<65>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<64> sich der
|
||||
<EFBFBD>berwachte Proze<7A> beendet. Dabei w<>rde \texttt{wait} bis in alle Ewigkeit
|
||||
warten, w<>re da nicht der Watchdog in der Subshell. Wenn dem die Ausf<73>hrung zu
|
||||
lange dauert, sorgt er daf<61>r da<64> der Proze<7A> beendet wird.
|
||||
|
||||
Kommt der <20>berwachte Proze<7A> aber rechtzeitig zur<75>ck, sorgt \texttt{kill} in
|
||||
Zeile 8 daf<61>r da<64> der Wachhund `eingeschl<68>fert' wird.
|
||||
|
||||
Auf diese Weise ist sichergestellt, da<64> der \texttt{ping} auf keinen Fall
|
||||
l<EFBFBD>nger als f<>nf Sekunden l<>uft.
|
||||
|
||||
|
Reference in New Issue
Block a user