Linux Deutsch

Datenströme in Linux: Standardeingabe, Standardausgabe und Standardfehlerausgabe

Lerne die Linux-Datenströme stdin, stdout und stderr, Dateideskriptoren, Umleitungen, Pipes und den praktischen Umgang mit Fehlermeldungen.

Linux-Programme verarbeiten Daten: Sie lesen Eingaben, führen eine Aufgabe aus und erzeugen Ergebnisse oder Fehlermeldungen. Die Shell stellt diese Ein- und Ausgaben als Datenströme bereit. Ein Datenstrom ist ein fortlaufender Kanal für Daten, die ein Programm empfängt oder ausgibt.

Dadurch muss ein Programm nicht fest auf eine bestimmte Quelle oder ein bestimmtes Ziel eingestellt sein. Ein Programm kann beispielsweise Daten aus der Tastatur, aus einer Datei oder von einem anderen Programm lesen. Seine Ausgabe kann im Terminal erscheinen, in einer Datei landen oder an ein weiteres Programm weitergereicht werden.

Inhaltsübersicht

  • Grundprinzip von Datenströmen
  • Die drei Standarddatenströme
  • Standardeingabe mit stdin
  • Standardausgabe mit stdout
  • Standardfehlerausgabe mit stderr
  • Umleitungen und Dateideskriptoren
  • Ausgaben zusammenführen oder verwerfen
  • Pipelines zwischen Programmen
  • Praktische Interpretation und Fehlersuche

Grundprinzip von Datenströmen

Ein Programm erhält Eingabedaten und erzeugt daraus eine Ausgabe. Die Shell verbindet das Programm beim Start mit drei vordefinierten Kanälen. Im normalen interaktiven Betrieb sind diese Kanäle mit dem Terminal verbunden:

  • Die Tastatur liefert Eingaben.
  • Normale Ergebnisse werden im Terminal angezeigt.
  • Fehlermeldungen werden ebenfalls im Terminal angezeigt.

Obwohl normale Ausgaben und Fehlermeldungen zunächst am gleichen Ort erscheinen, sind sie technisch getrennte Datenströme. Diese Trennung ist wichtig, wenn ein Skript nur Ergebnisse weiterverarbeiten oder Fehler in einem eigenen Protokoll speichern soll.

Die drei Standarddatenströme

Jeder Standarddatenstrom besitzt einen numerischen Dateideskriptor. Ein Dateideskriptor ist eine Kennung für einen geöffneten Ein- oder Ausgabekanal.

NameAbkürzungDateideskriptorStandardquelle oder -zielTypischer Inhalt
Standardeingabestdin0TastaturEingabedaten
Standardausgabestdout1TerminalReguläre Ergebnisse
Standardfehlerausgabestderr2TerminalFehlermeldungen

Die Zahlen werden vor allem bei Umleitungen sichtbar. Ohne Angabe eines Dateideskriptors beziehen sich <, > und >> normalerweise auf stdin oder stdout. Für stderr wird meist ausdrücklich die Kennung 2 angegeben.

Standardeingabe: stdin

Programme lesen Daten standardmäßig aus stdin, also aus dem Datenstrom mit Dateideskriptor 0. In einem interaktiven Terminal ist die Tastatur die Quelle dieses Stroms. Ein Programm wie sort wartet deshalb auf Eingabe, wenn ihm weder Dateinamen noch eine Pipe übergeben werden.

Mit der Eingabeumleitung < ersetzt die Shell die Tastatur als Quelle durch eine Datei:

sort < namen.txt

sort liest hier die Zeilen aus namen.txt über stdin und schreibt die sortierte Liste über stdout. Die Datei wird nicht als spezielles Wissen von sort benötigt; die Shell stellt ihren Inhalt als Standardeingabe bereit.

Eine weitere mögliche Quelle ist die Ausgabe eines anderen Befehls. Das geschieht mit einer Pipe und wird weiter unten erklärt.

Standardausgabe: stdout

Reguläre Ergebnisse schreibt ein Programm standardmäßig in stdout, den Datenstrom mit Dateideskriptor 1. Im interaktiven Terminal wird stdout sichtbar angezeigt.

Mit > wird stdout in eine Datei umgeleitet:

ls > dateiliste.txt

Die normale Ausgabe von ls erscheint dann nicht im Terminal, sondern wird in dateiliste.txt gespeichert.

Der Operator > legt die Datei bei Bedarf an. Existiert sie bereits, wird ihr bisheriger Inhalt überschrieben. Das kann zu Datenverlust führen. Prüfe daher vor der Ausführung das Ziel und den Dateinamen.

Mit >> wird stdout am Ende einer bestehenden Datei ergänzt:

date >> protokoll.txt

Die aktuelle Datums- und Zeitangabe wird an protokoll.txt angehängt. Ist die Datei noch nicht vorhanden, wird sie angelegt.

Standardfehlerausgabe: stderr

Fehlermeldungen werden normalerweise über stderr, den Datenstrom mit Dateideskriptor 2, ausgegeben. stderr erscheint zunächst ebenfalls im Terminal, ist aber nicht dasselbe wie stdout.

Das wird sichtbar, wenn stdout umgeleitet wird:

ls vorhandene_datei fehlende_datei > ausgabe.txt

Die erfolgreiche Ausgabe wird in ausgabe.txt gespeichert. Die Fehlermeldung für fehlende_datei kann trotzdem im Terminal erscheinen, weil nur stdout umgeleitet wurde. stderr bleibt unverändert mit dem Terminal verbunden.

Mit 2> wird stderr in eine eigene Datei umgeleitet:

ls vorhandene_datei fehlende_datei > ausgabe.txt 2> fehler.txt

Damit stehen reguläre Ergebnisse und Fehlermeldungen getrennt zur Verfügung. Das ist nützlich für Protokolle, Shell-Skripte und Programme, die nur gültige Ergebnisse weiterverarbeiten sollen.

Umleitungen mit Shell-Operatoren

Eine Umleitung verändert durch die Shell die Quelle oder das Ziel eines Datenstroms. Die Shell verarbeitet die Umleitung, bevor das Programm ausgeführt wird.

OperatorWirkungBeispielWichtiger Hinweis
<Datei als stdin verwendensort < namen.txtDas Programm liest aus der Datei statt von der Tastatur.
>stdout in eine Datei schreibenls > dateiliste.txtEine vorhandene Datei wird überschrieben.
>>stdout an eine Datei anhängendate >> protokoll.txtDer bisherige Inhalt bleibt erhalten.
2>stderr in eine Datei schreibenbefehl 2> fehler.txtNur Dateideskriptor 2 wird umgeleitet.
2>&1stderr zum aktuellen Ziel von stdout leitenbefehl > gesamt.txt 2>&1Die Reihenfolge ist entscheidend.
|stdout an stdin des nächsten Befehls übergebenbefehl1 | befehl2stderr wird ohne zusätzliche Umleitung nicht weitergereicht.
2> /dev/nullstderr verwerfenbefehl 2> /dev/nullFehlermeldungen sind danach nicht mehr verfügbar.

Dateideskriptoren ausdrücklich angeben

Die Schreibweise 2> fehler.txt bedeutet: Leite den Datenstrom mit der Nummer 2, also stderr, in die Datei um. Entsprechend könnte stdout ausdrücklich als 1> ausgabe.txt geschrieben werden. In der Praxis wird die 1 bei stdout meist weggelassen:

befehl 1> ausgabe.txt
befehl > ausgabe.txt

Beide Varianten leiten stdout um.

Reihenfolge kombinierter Umleitungen

Um stdout und stderr gemeinsam in einer Datei zu speichern, wird üblicherweise folgende Reihenfolge verwendet:

befehl > gesamtausgabe.txt 2>&1

Zuerst zeigt stdout auf gesamtausgabe.txt. Danach wird stderr auf das aktuelle Ziel von stdout verwiesen. So landen beide Ströme in derselben Datei.

Die Reihenfolge ist nicht beliebig. Bei befehl 2>&1 > gesamtausgabe.txt wird stderr zunächst an das ursprüngliche stdout-Ziel, meist das Terminal, gebunden. Die spätere Umleitung von stdout ändert stderr dann nicht mehr. Fehlermeldungen können deshalb weiterhin im Terminal erscheinen.

Ausgaben trennen, zusammenführen oder verwerfen

stdout und stderr getrennt speichern

befehl > ausgabe.txt 2> fehler.txt

Diese Variante ist besonders übersichtlich: ausgabe.txt enthält die regulären Ergebnisse, fehler.txt enthält Fehlermeldungen. Ein Skript kann dadurch die Ergebnisse unabhängig vom Fehlerprotokoll auswerten.

Beide Datenströme in einer Datei sammeln

befehl > gesamtausgabe.txt 2>&1

Die gemeinsame Datei eignet sich beispielsweise für einen vollständigen Laufbericht. Beachte, dass die Ausgaben aus unterschiedlichen Strömen stammen und ihre zeitliche Reihenfolge bei komplexen Programmen nicht immer so erscheint, wie sie im Terminal sichtbar war.

Fehlermeldungen mit /dev/null verwerfen

/dev/null ist ein spezielles Gerät. Daten, die dorthin geschrieben werden, werden verworfen. Es kann sinnvoll sein, erwartete oder uninteressante Fehlermeldungen auszublenden:

find / -name beispiel 2> /dev/null

Hier werden Zugriffsfehler verworfen. Reguläre Suchtreffer, die über stdout ausgegeben werden, bleiben sichtbar. Verwende diese Technik bewusst: Ein verworfener Fehler kann später nicht mehr untersucht werden.

Pipelines als Verbindung zwischen Programmen

Der Pipe-Operator | verbindet zwei Programme. Er leitet stdout des linken Befehls als stdin an den rechten Befehl weiter:

printf '%s
' Birne Apfel Kirsche | sort

printf erzeugt drei Zeilen. Die Pipe übergibt diese Zeilen an stdin von sort. sort verarbeitet sie und schreibt die alphabetische Reihenfolge auf stdout.

Pipelines eignen sich zur schrittweisen Verarbeitung von Textdaten. Jeder Befehl kann sich auf eine klar abgegrenzte Aufgabe konzentrieren: Ein Befehl erzeugt Daten, ein anderer filtert oder sortiert sie. Weitere typische Filter sind beispielsweise grep, cut und uniq.

Wichtig: Eine Pipe übergibt standardmäßig nur stdout. stderr des linken Befehls wird nicht durch die Pipe geleitet:

befehl1 | befehl2

Wenn stderr fachlich sinnvoll ebenfalls verarbeitet werden soll, muss es vorher zu stdout zusammengeführt werden:

befehl1 2>&1 | befehl2

Damit erhält befehl2 sowohl reguläre Ausgaben als auch Fehlermeldungen über stdin. Das sollte nur verwendet werden, wenn der nächste Befehl beide Arten von Text verarbeiten soll.

Praktische Interpretation von Befehlen

Woher bekommt ein Befehl seine Daten?

Ein Befehl kann Daten auf drei typische Arten erhalten:

  • Argumente: Die Daten oder Dateinamen stehen direkt in der Befehlszeile, etwa bei ls datei.txt.
  • Dateien als Argumente: Viele Programme öffnen eine genannte Datei selbst, etwa sort namen.txt.
  • stdin: Eine Datei wird mit < umgeleitet oder Daten kommen über eine Pipe, etwa sort < namen.txt oder printf '%s ' C A B | sort.

Diese Varianten sind nicht immer vollständig austauschbar, weil Programme Argumente und stdin unterschiedlich behandeln können. Die Dokumentation des jeweiligen Befehls zeigt, welche Eingabeformen unterstützt werden.

Warum bleibt etwas im Terminal sichtbar?

Eine Ausgabe kann im Terminal sichtbar sein, weil stdout oder stderr dorthin verbunden ist. Sichtbarkeit bedeutet nicht, dass der Datenstrom nicht umgeleitet werden kann. Mit > änderst du das Ziel von stdout; mit 2> änderst du das Ziel von stderr.

Nach befehl > ausgabe.txt können daher weiterhin Fehlermeldungen erscheinen. Die normale Ausgabe wurde umgeleitet, stderr jedoch nicht. Für beide Ströme verwendest du beispielsweise:

befehl > gesamtausgabe.txt 2>&1

Fehlersuche bei Datenströmen

  • Datei enthält den vorherigen Inhalt nicht mehr: Der Operator > überschreibt vorhandene Dateien. Verwende zum Ergänzen >> und prüfe das Ziel vor der Ausführung.
  • Fehler erscheinen trotz > ausgabe.txt im Terminal: Nur stdout wurde umgeleitet. Nutze 2> fehler.txt für eine getrennte Datei oder 2>&1 zum Zusammenführen.
  • Eine Pipeline verarbeitet Fehlermeldungen nicht: | übergibt standardmäßig nur stdout. Verwende bei Bedarf befehl 2>&1 | weiterer_befehl.
  • Die gemeinsame Datei ist nicht vollständig: Prüfe die Reihenfolge. Die übliche Form ist befehl > datei 2>&1.
  • sort wartet scheinbar endlos: Ohne Datei, Pipe oder Eingabeumleitung liest sort von stdin, also von der Tastatur. Gib Text ein und beende die Eingabe mit dem EOF-Signal deiner Shell oder verwende eine Datei beziehungsweise Pipe.

Prüfungsrelevante Merksätze

  1. stdin hat den Dateideskriptor 0, stdout den Dateideskriptor 1 und stderr den Dateideskriptor 2.
  2. < ändert die Quelle von stdin.
  3. > überschreibt eine Datei; >> hängt Daten an.
  4. 2> leitet nur stderr um.
  5. > datei 2>&1 speichert stdout und stderr gemeinsam in einer Datei.
  6. Eine Pipe leitet standardmäßig nur stdout des linken Befehls an stdin des rechten Befehls.
  7. /dev/null verwirft Daten, die dorthin geschrieben werden.

Für angrenzende Grundlagen eignen sich die Lektionen Die Bash-Shell, Grundlegende Linux-Befehle und Arbeiten mit Dateien. Für die weitere Textverarbeitung sind außerdem der cut-Befehl und wc zum Zählen von Wörtern und Zeilen hilfreich.