Aufgrund eines aktuellen Anlasses und der Tatsache, dass Fragen zu Ausgabeumlenkung und Pipes immer wieder auftauchen und somit quasi FAQs darstellen, habe ich mal einen kleinen Text geschrieben, der gaengige Faelle hoffentlich einleuchtend erklaert. Sollte er als sinnvoll und hilfreich angesehen werden, so bitte ich ein Mitglied des "inofficial SuSE-FAQ" Teams, den Text nach evtl. noetiger Korrekturen und Verbesserungen in die FAQ aufzunehmen. Ich hoffe, ich habe keine all zu grossen Bugs eingebaut... Viele Gruesse, Thomson ********************************************************************* Ausgabeumlenkung (shell redirection) und Pipes ============================================== Keywords: Ausgabeumlenkung, Redirection, Standardausgabe, Standardfehlerausgabe, stdout, stderr, Dateideskriptor, File Descriptor, Pipe Immer wieder taucht die Frage auf, wie die Standardausgabe (stdout) bzw. die Standardfehlerausgabe (stderr) eines Programms umgeleitet werden kann (z.B. in eine Datei) oder wie die entsprechende Ausgabe an ein anderes Programm weitergeleitet werden kann (Pipe). Im Manual zur Shell "bash" (siehe "man bash") findet man dieses Thema im Kapitel "Redirection" - allerdings ist das Thema dort kurz und buendig dargestellt und ein paar Feinheiten sind auf den ersten Blick nicht unbedingt ersichtlich. Hier sollen nun ein paar typische Faelle der Ausgabeumlenkung, die immer wieder auftauchen, naeher erlaeutert werden. Mit Umlenkung der Standardeingabe werden wir uns nicht befassen. Um die Erklaerungen besser nachvollziehen zu koennen, benutzen wir ein kleines Shell-Skript, das wie folgt aussieht: #!/bin/bash echo "Dies ist eine Ausgabe auf stdout." >&1 echo "Dies ist eine Ausgabe auf stderr." >&2 Beim Aufruf dieses kleinen Skriptes, das wir fortan mit "redir.sh" betiteln wollen, werden zwei Ausgaben produziert, eine auf die Standardausgabe, d.h. stdout, und eine andere auf die Standardfehlerausgabe, d.h. stderr: $> redir.sh Dies ist eine Ausgabe auf stdout. Dies ist eine Ausgabe auf stderr. $> (Anmerkung: "$>" stellt den Shell-Prompt dar) Die Syntax aus oben angegebenem Skript sollte gleich klarer werden - zum momentanen Zeitpunkt nehmen wir das mal als gegeben hin. Ohne Ausgabeumlenkung werden standardmaessig sowohl stdout als auch stderr auf dem Bildschirm (Konsole) ausgegeben, was man leicht beim Aufruf des Skriptes verfolgen kann (siehe oben). Moechte man nun die Standardausgabe (stdout) umleiten (z.B. in eine Datei, in unserem folgenden Beispiel nehmen wir statt einer Datei aber der Einfachheit halber das Device /dev/null, also den Muelleimer des Systems), so kann man das mit dem Operator "1>" bzw. ">" tun - beide Operatoren sind identisch, letztere Schreibweise ist etwas kuerzer. Die Zahl "1" steht dabei fuer einen sog. Dateideskriptor (file descriptor), der auch im Folgenden immer wieder auftaucht. Vordefiniert sind die folgenden Dateideskriptoren: 0 = Standardeingabe (stdin) 1 = Standardausgabe (stdout) 2 = Standardfehlerausgabe (stderr) Man beachte, dass hier zwischen dem Dateideskriptor und dem Groesser-Zeichen kein Leerzeichen stehen darf, sonst produziert der Befehl evtl. nicht das, was man erwarten wuerde. Referenzieren kann man die Dateideskriptoren mit "&0" bzw. "&1" bzw. &2". Das wird sicherlich im Laufe der folgenden Beispiele klar. In unserem Beispiel mit redir.sh passiert nun folgendes: $> redir.sh 1> /dev/null Dies ist eine Ausgabe auf stderr. $> Die Standardausgabe wird also im Muelleimer /dev/null entsorgt und taucht daher nicht mehr auf dem Bildschirm auf, uebrig bleibt nur die Standardfehlerausgabe. Identisch dazu waere die Eingabe von $> redir.sh > /dev/null als etwas kuerzere Schreibweise - sie produziert die gleiche Ausgabe wie oben. Moechte man die Standardfehlerausgabe (stderr) umleiten, so kann man in logischer Fortsetzung des bisherigen Beispiels den Operator "2>" benutzen. Die Angabe des Dateideskriptors kann hier natuerlich nicht weggelassen werden (es gibt also keine verkuerzte Schreibweise), da wir ja sonst wieder den Operator fuer die Umleitung von stdout reproduzieren wuerden. Unser Beispiel liefert hier $> redir.sh 2> /dev/null Dies ist eine Ausgabe auf stdout. $> was jedem einleuchten sollte. Die Standardfehlerausgabe wird im Muelleimer /dev/null entsorgt und uebrig bleibt die Standardausgabe auf dem Bildschirm (Konsole). Moechte man sowohl stdout als auch stderr *gleichzeitig* umlenken, so kann man hierfuer den Operator "&>" benutzen. Auch hier wiederum achte man darauf, keine Leerzeichen zwischen dem &- und dem >-Zeichen einzufuegen! $> redir.sh &> /dev/null $> Da nun sowohl stdout als auch stderr gleichzeitig im Muelleimer /dev/null entsorgt werden, erscheint keine Ausgabe mehr auf dem Bildschirm (Konsole). Auch dieses Beispiel sollte einleuchten. Es kann uebrigens auch durch die folgende Eingabe realisiert werden (die erste oben angegebene Variante sollte aber vorgezogen werden): $> redir.sh >& /dev/null Kommen wir nun aber zu interessanteren Faellen. Dazu betrachten wir uns zunaechst die beiden folgenden Versuche: 1. Versuch: $> redir.sh > /dev/null 2>&1 $> 2. Versuch: $> redir.sh 2>&1 > /dev/null Dies ist eine Ausgabe auf stderr. $> Wie man sofort anhand der beiden Versuche sieht, haengt die Wirkung von Ausgabeumleitungen von der Reihenfolge ab, wie sie angegeben werden. Werden beim 1. Versuch sowohl stdout als auch stderr in den Muelleimer umgeleitet (deswegen erscheint keine Ausgabe mehr auf dem Bildschirm), so wird beim 2. Versuch lediglich stdout in den Muelleimer umgelenkt, stderr erscheint weiterhin auf dem Bildschirm (Konsole). Insbesondere mag nun dieser 2. Versuch auf den ersten Blick voellig unlogisch erscheinen und nicht das produzieren, was manche erwartet haetten. Die Erklaerung ist aber relativ einfach: Um zu verstehen, was passiert, muss man wissen, dass die Wirkung der Ausgabeumlenkung von links nach rechts (also in der natuerlichen Reihenfolge wie man auch liest) auszuwerten ist. Beim 1. Versuch wird zunaechst die Standardausgabe (stdout) ueber den Operator ">" in den Muelleimer umgeleitet. Im Anschluss daran wird stderr auf stdout umgeleitet (dubliziert), oder genauer: stderr wird auf den Ort umgeleitet, auf den stdout *zum Zeitpunkt der Umlenkung* zeigt. Das ist hier /dev/null. Daher landet auch die Standardfehlerausgabe im Muelleimer und auf dem Bildschirm bleibt keine Ausgabe uebrig. Es erscheint schlicht wieder der Shell-Prompt. Beim 2. Versuch wird zunaechst stderr auf stdout umgeleitet (dubliziert), oder anders ausgedrueckt: stderr wird auf den Ort umgeleitet, auf den stdout zum Zeitpunkt der Umleitung zeigt - das ist der Bildschirm (Konsole). Man muss nun realisieren, dass die Dateideskriptoren zu stdout und stderr trotz der Umleitung weiterhin als getrennt zu betrachten sind (weswegen man manchmal auch eher von duplizieren redet). Im Anschluss an die erste Umleitung wird nun die Standardausgabe ueber den Operator ">" in den Muelleimer gelenkt. Der Dateideskriptor "2", also stderr, zeigt aber weiterhin auf die Bildschirmausgabe (dorthin wurde er ja vorher dubliziert), sodass der Text zu stderr wie oben im Beispiel zu sehen auf der Konsole erscheint. Wichtig zu realisieren ist also, dass unter Beachtung der Reihenfolge der Umleitungen zunaechst stderr auf stdout und dann stdout (und *nur* stdout) nach /dev/null geleitet wird. Kommen wir nun zu sog. Pipes, die durch den Operator "|" realisiert werden: eine Pipe verknuepft die Standardausgabe eines Programms A mit der Standardeingabe eines Programms B. Man beachte, dass hier nur von Standardausgabe, nicht von Standardfehlerausgabe, die Rede ist. Durch $> progA | progB wird also lediglich stdout von progA an progB geleitet - eine evtl. stattfindende Fehlerausgabe auf stderr von progA wuerde auf dem Bildschirm (Konsole) erscheinen. Soll sowohl stdout als auch stderr von progA an progB geleitet werden, so muss man $> progA 2>&1 | progB benutzen, d.h. die Standardfehlerausgabe wird vor der Pipe auf der Standardausgabe dubliziert. Das Problem hat man z.B., wenn man die komplette Ausgabe eines make-Befehls (also die normal durchgefuehrten Aktionen *und* evtl. auftauchende Fehler) protokollieren moechte. Das geht z.B. sehr elegant mit dem Konstrukt $> make target 2>&1 | tee make.log Fuer Details, siehe u.a. "man tee". Moechte man nur stderr von progA nach progB leiten, so ist (wie oben ausfuehrlich erlaeutert) die folgende Sequenz zu nutzen: $> progA 2>&1 > /dev/null | progB Kurze Erklaerung: Hier wird zunaechst stderr auf stdout umgeleitet (d.h. stderr zeigt nun dorthin, wo auch stdout hin zeigt), im Anschluss daran wird stdout nach /dev/null umgeleitet. Uebrig bleibt also nur das urspruengliche stderr, was nun (da vorher korrekt dubliziert) ueber die Pipe an progB weiter geleitet wird. Alle Klarheiten beseitigt?? Einen Operator "2|", wie es manchen nun evtl. sinnvoll erscheinen koennte, um die Standardfehlerausgabe durch eine Pipe zu schicken, gibt es uebrigens nicht. Hat man das Prinzip einmal verstanden, so kann man gaengige Konstrukte zur Ausgabeumlenkung systematisch analysieren. Es sollte nun zum Beispiel nach obigen Erlaeuterungen klar sein, warum ein Konstrukt wie $> progA 1>&2 | progB nie funktionieren kann! Beliebige weitere Beispiele (sinnvolle und weniger sinnvolle) lassen sich natuerlich leicht angeben. Es sollte nun aber hoffentlich die Manual-Seite "man bash" und das dortige Kapitel "Redirection" etwas einleuchtender erscheinen. Thomas Hertweck, 20. Juli 2004