2003-04-11 15:05:25 +00:00
|
|
|
|
% $Id$
|
2001-07-02 12:52:18 +00:00
|
|
|
|
\chapter{Schmutzige Tricks :-)}
|
|
|
|
|
|
|
|
|
|
Eigentlich sind diese Tricks gar nicht so schmutzig. Hier ist lediglich eine
|
|
|
|
|
Sammlung von Beispielen, die genial einfach oder genial gut programmiert sind.
|
|
|
|
|
Das bedeutet nicht, da<64> jeder Shell-Programmierer diese Techniken benutzen
|
|
|
|
|
sollte. Ganz im Gegenteil. Einige Mechanismen bergen Gefahren, die nicht auf
|
|
|
|
|
den ersten Blick erkennbar sind.
|
|
|
|
|
|
|
|
|
|
Mit anderen Worten: \textbf{Wenn Du diese Techniken nicht verstehst, dann
|
|
|
|
|
benutze sie nicht!}
|
|
|
|
|
|
|
|
|
|
Die Intention hinter diesem Abschnitt ist es, dem gelangweilten Skripter etwas
|
|
|
|
|
interessantes zum Lesen zu geben. Das inspiriert dann vielleicht dazu, doch
|
|
|
|
|
einmal in den fortgeschrittenen Bereich vorzusto<74>en, neue Techniken
|
|
|
|
|
kennenzulernen. Au<41>erdem kann das Wissen <20>ber gewisse Techniken eine gro<72>e
|
|
|
|
|
Hilfe beim Lesen fremder Skripte darstellen, die eventuell von diesen Techniken
|
|
|
|
|
Gebrauch machen.
|
|
|
|
|
|
|
|
|
|
Diese Techniken sind nicht `auf meinem Mist gewachsen', sie stammen vielmehr
|
|
|
|
|
aus Skripten, die mir im Laufe der Zeit in die Finger gekommen sind. Ich danke
|
|
|
|
|
an dieser Stelle den klugen K<>pfen, die sich solche Sachen einfallen lassen
|
|
|
|
|
haben.
|
|
|
|
|
|
|
|
|
|
\section{Die Tar-Br<42>cke}
|
|
|
|
|
|
2004-11-05 16:20:53 +00:00
|
|
|
|
TODO!!! tar-Br<42>cke
|
2001-07-02 12:52:18 +00:00
|
|
|
|
|
2004-11-12 12:07:32 +00:00
|
|
|
|
%ssh 192.168.2.1 tar clf - / | (cd /mnt; tar xf - )
|
2004-12-02 13:54:06 +00:00
|
|
|
|
%tar cf - $j | rsh $i "(mkdir -p $PWD ;cd $PWD; tar xf -)"
|
2004-11-12 12:07:32 +00:00
|
|
|
|
|
2001-07-02 12:52:18 +00:00
|
|
|
|
|
|
|
|
|
\section{Binaries inside}
|
|
|
|
|
|
2004-11-05 16:20:53 +00:00
|
|
|
|
TODO!!! binaries inside
|
2001-07-02 12:52:18 +00:00
|
|
|
|
|
|
|
|
|
\subsection{Bin<EFBFBD>re Here-Dokumente}
|
|
|
|
|
|
2004-11-05 16:20:53 +00:00
|
|
|
|
TODO!!! bin<69>re Here-Dokumente
|
2001-07-02 12:52:18 +00:00
|
|
|
|
|
|
|
|
|
\subsection{Schwanz ab!}
|
|
|
|
|
|
2004-11-05 16:20:53 +00:00
|
|
|
|
TODO!!! Schwanz ab
|
2001-07-02 12:52:18 +00:00
|
|
|
|
|
|
|
|
|
\section{Dateien, die es nicht gibt}
|
|
|
|
|
|
2004-11-05 16:20:53 +00:00
|
|
|
|
TODO!!! Dateien, die es nicht gibt
|
2001-07-02 12:52:18 +00:00
|
|
|
|
|
|
|
|
|
\subsection{Speichern in nicht existente Dateien}
|
|
|
|
|
|
2004-11-05 16:20:53 +00:00
|
|
|
|
TODO!!! Speichern in nicht existente Dateien
|
2001-07-02 12:52:18 +00:00
|
|
|
|
|
2004-12-02 13:54:06 +00:00
|
|
|
|
\subsection{Subshell-Schleifen vermeiden}\label{subshellschleifen}
|
2002-03-25 13:48:40 +00:00
|
|
|
|
|
|
|
|
|
Wir wollen ein Skript schreiben, das die \texttt{/etc/passwd} liest und dabei
|
|
|
|
|
z<EFBFBD>hlt, wie viele Benutzer eine UID kleiner als 100 haben.
|
|
|
|
|
|
|
|
|
|
Folgendes Skript funktioniert nicht:
|
|
|
|
|
|
2004-12-10 14:38:03 +00:00
|
|
|
|
\begin{lstlisting}
|
2002-03-25 13:48:40 +00:00
|
|
|
|
#!/bin/sh
|
|
|
|
|
count=0
|
|
|
|
|
cat /etc/passwd | while read i; do
|
|
|
|
|
uid=`echo $i | cut -f 3 -d:`
|
|
|
|
|
if [ $uid -lt 100 ]; then
|
|
|
|
|
count=`expr $count + 1`
|
|
|
|
|
echo $count
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
echo Es sind $count Benutzer mit einer ID kleiner 100 eingetragen
|
2004-12-10 14:38:03 +00:00
|
|
|
|
\end{lstlisting}
|
2002-03-25 13:48:40 +00:00
|
|
|
|
|
|
|
|
|
Was ist passiert?
|
|
|
|
|
|
|
|
|
|
Dieses Skript besteht im Wesentlichen aus einer Pipe. Wir haben ein
|
2004-11-19 12:09:34 +00:00
|
|
|
|
\texttt{cat}-Kom\-man\-do, das den Inhalt der \texttt{/etc/passwd} durch eben
|
|
|
|
|
diese Pipe an eine Schleife <20>bergibt. Das \texttt{read}-Kommando in der
|
|
|
|
|
Schleife liest die einzelnen Zeilen aus, dann folgt ein Bi<42>chen Auswertung.
|
2002-03-25 13:48:40 +00:00
|
|
|
|
|
|
|
|
|
Es ist zu beobachten, da<64> bei der Ausgabe in Zeile 7 die Variable
|
|
|
|
|
\texttt{\$count} korrekte Werte enth<74>lt. Um so unverst<73>ndlicher ist es, da<64> sie
|
|
|
|
|
nach der Vollendung der Schleife wieder den Wert 0 enth<74>lt.
|
|
|
|
|
|
|
|
|
|
Das liegt daran, da<64> diese Schleife als Teil einer Pipe in einer Subshell
|
|
|
|
|
ausgef<EFBFBD>hrt wird. Die Variable \texttt{\$count} steht damit in der Schleife
|
|
|
|
|
praktisch nur lokal zur Verf<72>gung, sie wird nicht an das umgebende Skript
|
2004-11-05 16:20:53 +00:00
|
|
|
|
`hochgereicht'.
|
2002-03-25 13:48:40 +00:00
|
|
|
|
|
|
|
|
|
Neben der Methode in \ref{daten_hochreichen} bietet sich hier eine viel
|
|
|
|
|
einfachere L<>sung an:
|
|
|
|
|
|
2004-12-10 14:38:03 +00:00
|
|
|
|
\begin{lstlisting}
|
2002-03-25 13:48:40 +00:00
|
|
|
|
#!/bin/sh
|
|
|
|
|
count=0
|
|
|
|
|
while read i; do
|
|
|
|
|
uid=`echo $i | cut -f 3 -d:`
|
|
|
|
|
if [ $uid -lt 100 ]; then
|
|
|
|
|
count=`expr $count + 1`
|
|
|
|
|
echo $count
|
|
|
|
|
fi
|
|
|
|
|
done < /etc/passwd
|
|
|
|
|
echo Es sind $count Benutzer mit einer ID kleiner 100 eingetragen
|
2004-12-10 14:38:03 +00:00
|
|
|
|
\end{lstlisting}
|
2002-03-25 13:48:40 +00:00
|
|
|
|
|
|
|
|
|
Hier befindet sich die Schleife nicht in einer Pipe, daher wird sie auch nicht
|
|
|
|
|
in einer Subshell ausgef<65>hrt. Man kann auf das \texttt{cat}-Kommando verzichten
|
|
|
|
|
und den Inhalt der Datei durch die Umlenkung in Zeile 9 direkt auf die
|
|
|
|
|
Standardeingabe der Schleife (und somit auf das \texttt{read}-Kommando) legen.
|
|
|
|
|
|
|
|
|
|
\subsection{Daten aus einer Subshell hochreichen}\label{daten_hochreichen}
|
2001-07-02 12:52:18 +00:00
|
|
|
|
|
2004-11-05 16:20:53 +00:00
|
|
|
|
TODO!!! Daten aus einer Subshell hochreichen
|
2002-03-22 16:34:33 +00:00
|
|
|
|
|
|
|
|
|
\subsection{Dateien gleichzeitig lesen und schreiben}
|
|
|
|
|
|
|
|
|
|
Es kommt vor, da<64> man eine Datei bearbeiten m<>chte, die hinterher aber wieder
|
|
|
|
|
unter dem gleichen Namen zur Verf<72>gung stehen soll. Beispielsweise sollen alle
|
|
|
|
|
Zeilen aus einer Datei entfernt werden, die nicht das Wort \textit{wichtig}
|
|
|
|
|
enthalten.
|
|
|
|
|
|
|
|
|
|
Der erste Versuch an der Stelle wird etwas in der Form
|
|
|
|
|
|
2004-12-10 14:38:03 +00:00
|
|
|
|
\lstinline|grep wichtig datei.txt > datei.txt|
|
2002-03-22 16:34:33 +00:00
|
|
|
|
|
|
|
|
|
sein. Das kann funktionieren, es kann aber auch in die sprichw<68>rtliche Hose
|
|
|
|
|
gehen. Das Problem an der Stelle ist, da<64> die Datei an der Stelle gleichzeitig
|
|
|
|
|
zum Lesen und zum Schreiben ge<67>ffnet wird. Das Ergebnis ist undefiniert.
|
|
|
|
|
|
|
|
|
|
Eine Elegante L<>sung besteht darin, einen Filedeskriptor auf die Quelldatei zu
|
|
|
|
|
legen und diese dann zu l<>schen. Die Datei wird erst dann wirklich aus dem
|
|
|
|
|
Dateisystem entfernt, wenn kein Deskriptor mehr auf sie zeigt. Dann kann aus
|
|
|
|
|
dem gerade angelegten Deskriptor gelesen werden, w<>hrend eine neue Datei unter
|
|
|
|
|
dem alten Namen angelegt wird:
|
|
|
|
|
|
2004-12-10 14:38:03 +00:00
|
|
|
|
\begin{lstlisting}
|
2002-03-22 16:34:33 +00:00
|
|
|
|
#!/bin/sh
|
|
|
|
|
FILE=datei.txt
|
|
|
|
|
exec 3< "$FILE"
|
|
|
|
|
rm "$FILE"
|
|
|
|
|
grep "wichtig" <&3 > "$FILE"
|
2004-12-10 14:38:03 +00:00
|
|
|
|
\end{lstlisting}
|
2002-03-22 16:34:33 +00:00
|
|
|
|
|
2002-03-25 13:48:40 +00:00
|
|
|
|
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.
|
2004-11-05 16:20:53 +00:00
|
|
|
|
|
|
|
|
|
\section{Auf der Lauer: Wachhunde}
|
|
|
|
|
|
|
|
|
|
TODO!!! Auf der Lauer: Wachhunde
|
|
|
|
|
|