Umleitungen

Aus wiki.archlinux.de

Eine der Stärken von Linux ist, dass Ein- und Ausgabe von Programmen umgeleitet werden können. Es gibt zwei wichtige Ausgabeleitungen (Kanäle genannt), dies sind STDERR, auf das alle Fehlermeldungen ausgegeben werden und STDOUT, auf das alle normalen Programm-Ausgaben ausgegeben werden. Zusätzlich gibt es noch den Standardeingabe-Kanal STDIN.

Bezeichnungen

Je nach Position an der ein Kanal angesprochen wird, hat er unterschiedliche Bezeichnungen, zudem werden die Kanäle je nach Position unterschiedlich referenziert.

Kanal Bezeichnung Schreibweise
STDIN Eingabeumleitung < oder <<
STDIN Standardeingabe -
STDOUT Ausgabeumleitung > oder >>
STDOUT Standardausgabe -
STDOUT Standardausgabe-Kanal 1 oder &1
STDERR Standard-Fehlerkanal 2 oder &2

In diesem Artikel werden der Standardeingabe-Kanal und der Standardausgabe-Kanal, so wie der Standard-Fehlerkanal immer als STDIN, STDOUT und STDERR bezeichnet.

Ausgabeumleitung

Prinzipiell können alle Ausgaben von Programmen entweder in eine Datei geschrieben, oder als Eingabe für weitere Programme verwendet werden. Die Angabe zur Umleitung kann zwar theoretisch an jeder Stelle eines Befehls gemacht werden, aus Gründen der Übersicht und der Logik sollte man eine Ausgabeumleitung aber nicht vor einer Eingabeumleitung oder dem eigentlichen Befehl definieren.

Der einfachste Fall einer Ausgabeumleitung ist das Umleiten von STDOUT in eine Textdatei, um so zum Beispiel eine Dateiliste zu erhalten.

ls -al > output

Dies schreibt die Namen der Dateien und Verzeichnisse im aktuellen Verzeichnis in die Datei output. Wenn die Datei noch nicht existiert, wird sie angelegt. Falls sie bereits existiert, wird sie überschrieben. Wenn man möchte, dass die Ausgabe an die Datei angehängt wird, benutzt man >>, anstatt >

ls -al >> output

>> output ls -al

Die zweite Zeile bewirkt das Selbe wie die erste Zeile, soll hier aber nur als Beispiel dafür dienen, dass es relativ irrelevant ist, an welcher Stelle eine Umleitung definiert wird (hier vor dem eigentlich umzuleitenden Befehl).

Die einfache Umleitung ohne Angabe des Kanals leitet immer STDOUT entsprechend um. Fehlermeldungen, die nach STDERR geschrieben werden, werden damit nicht umgeleitet. Es ist natürlich möglich, diese Fehlermeldungen auch umzuleiten

command 2>> error.log

Dies schreibt alle Fehlermeldungen des Programms command in die Datei error.log, wobei die Datei nicht überschrieben, sondern die Ausgabe immer an die Datei angehängt wird. Will man jetzt auch noch die Standard-Ausgabe umleiten, zum Beispiel nach /dev/null, weil das Programm als Cronjob läuft, und die Ausgabe unwichtig ist, geht dies mit folgendem Aufruf

command > /dev/null 2>> error.log

Dies schreibt STDOUT nach /dev/null, und STDERR in die Datei error.log. Es besteht auch die Möglichkeit, Kanäle „ineinanderzuleiten“. Dies kann nützlich sein, wenn man STDERR und STDOUT an die selbe Stelle umleiten möchte.

command 2>&1> /dev/null
command > /dev/null 2>&1

Dies leitet STDERR nach STDOUT (STDOUT wird hier mittels &1 referenziert. STDERR würde an dieser Stelle analog dazu mit &2 referenziert werden) um. STDOUT wird nach /dev/null umgeleitet. Das Resultat ist, dass weder Standard-Meldungen noch Fehlermeldungen von command ausgegeben werden.

Die zweite Zeile ist eine gängige, alternative Schreibweise dieses Aufrufs. Durch > /dev/null wird die STDOUT nach /dev/null geleitet, durch 2>&1 wird STDERR nach STDOUT geleitet (das ja nach /dev/null geleitet wird).

Alternativ kann man die Ausgabe auch in eine Datei umleiten. Es besteht ebenfalls die Möglichkeit, die zweite Umleitung ganz weg zu lassen.

command 2>&1

Dies leitet STDERR auf STDOUT um. Damit kann man nun mittels Pipe (siehe unten) die Standard-Ausgabe und die Fehlermeldungen in anderen Programmen als Eingabe benutzen.

Eingabeumleitung

Genau so, wie man Ausgaben umleiten kann, kann man auch Eingaben umleiten. Genauer gesagt kann man andere Eingabekanäle als STDIN verwenden, bzw. auf andere Art über STDIN etwas an ein Programm senden.

command < input

Dies leitet den Inhalt der Datei input an das Programm command weiter. Wie dieses Programm damit umgeht, ist dem Programmier des Programms überlassen. Wenn ein Programm keine derartigen Umleitungen annimmt, passiert für gewöhnlich gar nichts. Eine weitere Methode ist die Umleitung mittels <<EOF.

command <<EOF
> Zeile 1
> Zeile 2
> Zeile 3
> Zeile N
> EOF

Dies übergibt dem Programm command die Zeilen 1 bis N in dieser Reihenfolge, jeweils als eigenständige Eingabe. Beim Schlüsselwort EOF wird die Eingabeumleitung beendet. Statt EOF kann man nahezu beliebige Schlüsselwörter verwenden, EOF hat sich aber eingebürgert. Eine konkrete Anwendung könnte zum Beispiel ein Backup einer MySQL-Datenbank sein.

mysql -uUSER -pPASSWORD <<EOF
flush tables with read lock;
system lvcreate -l100%FREE -s -n dbbackup /dev/vgmysql/mysql;
unlock tables;
EOF

Dieser Aufruf sperrt die Datenbank für Schreibzugriffe, erstellt durch einen MySQL-internen Systemaufruf einen LVM-Snapshot an angegebener Position, und gibt die Tabellen wieder frei. Mittels weiterer Programme kann nun mit dem Snapshot gearbeitet werden (Backup erstellen, etc.), ohne, dass die eigentliche Tabelle beeinflusst wird.

Per Eingabeumleitung werden hier drei Befehle an den MySQL-client gesendet. Dieser interpretiert die Befehle so, als wenn sie direkt eingegeben worden wären.

Pipes

Mittels Pipes wird die Ausgabe eines Programms an ein anderes Programm weitergereicht. Das folgende Beispiel ist eigentlich ein Negativbeispiel, da grep bereits selbst über so eine Funktionalität verfügt, wird allerdings immer wieder gern hergenommen, um die Funktionsweise von Pipes zu demonstrieren, da es einfach und gut nachvollziehbar ist.

cat file | grep inhalt

Durch cat file wird der Inhalt der Datei file ausgegeben. Die Ausgabe wird an grep weitergeleitet, mittels dem die Ausgabe von cat, die nun Eingabe von grep geworden ist, nach dem Wort inhalt durchsucht wird. Es werden nun nur Zeilen ausgegeben, in denen inhalt steht.

Nur der Vollständigkeit halber, dies geht auch ohne cat:

grep inhalt file

Einige Programme behandeln STDIN und STDOUT in Verbindung mit Pipes ein wenig anders. Generell muss man hier die Manpage des jeweiligen Programms lesen, an welcher Stelle STDIN und STDOUT berücksichtigt werden, dann allerdings werden beide durch ein Minus-Zeichen angesprochen.

Ein konkretes Beispiel könnte das Aktualisieren eines Wallpapers sein. Das Wallpaper wird automatisch im Internet regelmäßig durch ein neues Wallpaper an selber Position ersetzt. Anstatt nun manuell jedes Mal das Wallpaper herunterzuladen und manuell jedes Mal einzustellen, kann man dies mittels wget und display aus dem ImageMagick-Paket machen.

wget http://example.com/zufallswallpaper.png -O - | display -window root -

Hier wird durch wget das Wallpaper heruntergeladen. Anstatt das Wallpaper standardmäßig abzuspeichern wird es mittels -O unter einer anderen Bezeichnung gespeichert, in diesem Fall ist dies die Standard-Ausgabe. Die Meldungen die wget sonst noch ausgibt, werden dabei nicht mit ausgegeben, sondern lediglich die heruntergeladene Datei.

Nach der Pipe wird display aufgerufen, durch -window root wird definiert, dass es das Bild auf dem Root-Fenster anzeigen soll, dies ist der Desktop. Durch - wird hier die Standard-Eingabe referenziert. In der Standard-Eingabe befindet sich das, was wget davor in seine Standard-Ausgabe geschrieben hat.

FIFO-Pipes

FIFO steht für „First In First Out“ (sinngemäß „Was zuerst eingegeben wird, wird zuerst wieder ausgegeben“). FIFO-Pipes können als Zwischenschritt genutzt werden, wenn ein Programm nicht mittels Ein-/Ausgabeiumleitung oder herkömmlicher Pipes verwendet werden kann, oder die Verwendung von Pipes aufgrund technischer Umstände nicht möglich ist.

Übertragene Darstellung

FIFO-Pipes kann man sich am besten als ein Rohr vorstellen. Auf beiden Seiten dieses Rohrs befinden sich Deckel. Wenn ein Programm nun auf einen FIFO-Pipe zugreift, wird der Deckel geöffnet, und das Programm schiebt die Ausgabe in das Rohr, bis die Ausgabe auf der anderen Seite an den Deckel stößt, dann wartet es.

Das Rohr bleibt dort so lange verschlossen, bis ein zweites Programm auf die FIFO-Pipe zugreift. Das zweite Programm öffnet den Deckel, und nimmt die Ausgabe des ersten Programms in der Reihenfolge in Empfang, wie sie das erste Programm in das Rohr geschoben hat. Die FIFO-Pipe ist nun offen.

Das Programm, das die Daten in Empfang nimmt, macht dies so lange, wie der „Deckel“ des eingebenden Programms geöffnet ist. Der Deckel dieses Programms schließt sich, wenn es alle Daten in das Rohr geschoben hat. Die FIFO-Pipe ist dann geschlossen. Das empfangende Programm registriert dies, und schließt die seinen Deckel ebenfalls.

Der Vergleich hinkt ein wenig, da alles in dem „Rohr“ in praktisch Echtzeit geschieht, und auch nichts in das Rohr „hineingeschoben“ wird, sondern das eine Programm einfach so lange mit der Ausgabe wartet, bis das andere Programm die Eingabe anfordert.

Verwendung

Wenn man eine FIFO-Pipe verwenden will, muss diese zuerst angelegt werden. Danach kann man sie ganz normal als Ausgabeumleitungs-Ziel verwenden.

mkfifo fifopipe
ls -l / > fifopipe &
cat fifopipe

Diese (eigentlich unnütze und nur der Demonstration dienende) Verwendung der FIFO-Pipe macht folgendes: Zuerst wird die FIFO-Pipe angelegt, dann wird die Ausgabe von ls -l / an diese FIFO-Pipe gesendet. Nun wird mittels cat die Pipe ausgegeben. Man sieht nun die Ausgabe von ls -l /.

Wenn man nun noch mal mittels cat die Inhalte der FIFO-Pipe ausgeben will, bekommt man nichts zurück, da der Deckel auf der anderen Seite ja nicht geöffnet ist. Erst wenn man auf der anderen Seite (zum Beispiel durch ein weiteres ls -l /, das man in die FIFO-Pipe umleitet) wieder etwas in das Rohr schiebt, erhält man eine Ausgabe.

FIFO-Dateien benutzen zum Weiterleiten der Inhalte Dateisystemfunktionen, normale Pipes benutzen Kernel-interne Puffer, wodurch diese Methode schneller ist. FIFO-Pipes können über Prozessgrenzen hinweg verwendet werden, wohingegen Pipes nur innerhalb des selben Elterprozesses verwendet werden können.

Siehe auch

  • Manpage stdio (vor allem für Programmierer interessant)
  • bash

Weblinks