Shells
Home
Erste Schritte mit der Bourne-Shell
Dateien zeilenweise zusammenfĂĽgen
Head or Tail
Schleifen und Zählen
Shell-Variablen
Pipes
Sortieren von Daten
Wer sucht der findet!
Praktische Shell-Skripte
Rechtschreibung
Schleifen und elegant Zählen
Texte formatieren mit fmt und fold
Umlenkung der Ein- und der Ausgaben
Klammer-Erweiterung
VerknĂĽpfen von Textdateien mittels SchlĂĽsselfeldern
Mit freundlicher Unterstützung von:
Linux-Kurse und Seminare
Veranstalter des
Linux-Tag am Bodensee 2007 und 2008
Kontakt
Haftung
Impressum
Problem Hilfe Startseite
|
Ein- und Ausgabeumlenkung
In diesem Artikel
behandeln wir an Hand vieler Beispiele die Ein- und Ausgabeumlenkung.
Am Ende des Abschnitts betrachten wir noch ein häufig diskutiertes
und vielfach nicht verstandenes Problem: Die Vertauschung der
Standardausgabe (stdout) und der Standardfehlerausgabe (stderr)
Wird ein Kommando in einer Shell ausgefĂĽhrt, so erscheint die Ausgabe
normalerweise im Terminal, in dem das Kommando aufgerufen wurde. Diese Ausgabe
wird auch als Standardausgabe bezeichnet.
Neben der Standardausgabe kennt Unix oder Linux noch zwei andere andere
Standardkommunikationswege, auch Standard-Datenströme oder Kanäle genannt:
Deskriptor |
Name |
Abkürzung |
Default |
0 |
Standardeingabe |
stdin |
Tastatur |
1 |
Standardausgabe |
stdout |
Bildschirm |
2 |
Standardfehler |
stderr |
Bildschirm |
In Linux können geöffnete Dateien sowohl über ihren Namen als auch über sogenannte Deskriptoren angesprochen werden. Für die Standarddatenströme sind dies, wie oben angegeben, die Deskriptoren 0 (für stdin), 1 (für stdout) und 2 (für stderr). Für weitere Dateien bietet die Shell noch die Deskriptoren von 3 bis 9.
Diagnose- und
Fehlermeldungen bzw. Warnungen werden über die
Standardfehlerausgabe ausgegeben, allerdings gibt es manchmal
leider auch Programme, die sich nicht an diese Konvention halten.
Die Standardeingabe, die Standardausgabe und die Standardfehlerausgabe kann man umlenken, d.h. man kann z.B. aus einer Datei statt von der Tastatur lesen oder in eine Datei statt auf das Terminal schreiben.
Für die Eingabeumlenkung
gibt es das "<"-Zeichen (Kleinerzeichen):
liest aus der Datei test.dat und schreibt das Ergebnis auf den
Bildschirm.
(Anmerkung: das Kommondo wc gibt die Anzahl der Zeichen, Wörter
und Zeilen der Datei test.txt zurück)
Obiges Kommando hätte man allerdings auch in der Form
aufrufen können
Für die Ausgabeumlenkung benutzt man das Größerzeichen,
also ">":
ls liefert den Inhalt des aktuellen Verzeichnisses, also die Namen
der Dateien und der Unterverzeichnisse. Die Umlenkung bewirk, dass das
Ergebnis in dir.txt gespeichert wird. Falls die Datei vor dem
Aufruf bereits existierte, wird sie mit dem neuen Inhalt
überschrieben. Allerdings nur, falls nicht die noclobber-Option
der Shell gesetzt ist, d.h. "set -o noclobber". Will man explizit immer
bereits existierende Dateien überschreiben, dann muss man "set +o
noclobber" auf der Shell absetzen.
Ist die noclobber-Option der Shell gesetzt, d.h. man kann keine Datei
überschreiben, dann gibt es noch die Möglichkeit eine
überschreibende Ausgabenumlenkung mit dem Befehl ">|" zu
erreichen:
echo
"abc wird auf jeden Fall überschrieben" >| abc |
In obigem Beispiel wird ein bereits existierendes abc auch
überschrieben, wenn die noclobber-Option gesetzt ist.
In vielen Fällen möchte man jedoch die Ausgaben eines
Kommandos an eine bestehende Datei hinten anhängen. Dies ist bei
Logdateien im Prinzip der Normalfall. Dies lässt sich ganz einfach
bewerkstellen, statt eines Größerzeichens benutzt man zwei
Größerzeichen ">>".
Im folgenden schauen wir uns an, wie man den Standarderror-Strom
umlenken kann. Dazu benutzt man
"2>":
wc
-l < test.dat 2> err.log |
Alle Fehlermeldungen aber nicht die regulären Ausgaben werden in
obigem Beispiel in die Datei err.log geschrieben.
Die 2 vor dem Größerzeichen lässt sich ganz logisch
erklären. Die allgemeine Syntax für die Dateiumlenkung lautet
"Deskriptor-Nummer" gefolgt von einem Kleiner- (Dateieinlesen)
oder einem Größerzeichen (Ausgabe in Datei).
<Kommando>
<Deskriptor-NUmmer><Umlenkungs-Operator> <Dateiname>
Achtung: die "<" und ">"-Zeichen in obigem Beispiel dienen nur
der Syntax-Beschreibung und dürfen nicht im konreten Befehl stehen. |
Zu Beginn dieses Artikels hätten wir also auch statt ">" auch
"1>" schreiben können. Prinzipiell ist ">" also so etwas wie
eine abgekürzte Schreibweise von "1>".
Fehler- und
Ausgabeumlenkung in eine Datei
Manchmal möchte oder muss man die sowohl die Fehler- als auch die
Standardausgabe in einer Datei protokollieren. Auch hierfür bietet
die Bash-Shell eine elegante Möglichkeit: Die Möglichkeit
einen Kanal an einen anderen anzuhängen.
allgemein:
<Deskriptor-Nummer1>>&<Deskriptor-Nummer2>
Beispiel: Fehlerkanal auf Standardausgabe umlenken und Standardausgabe
in Datei ausgeben:
ls
/etc > Inhalt_etc.txt 2>&1 |
Für Spezialisten:
Folgendes Problem:
Man möchte STDOUT und STDERR vertauschen, so dass z.B. nach einem
Pipezeichen ein Programm auf der Fehlerausgabe statt auf der Eingabe
arbeiten kann.
Um die folgenden Beispiele nachvollziehen zu können, kann man in
einem "Spiel"verzeichnis folgende Kommandos ausführen, um die
Dateien a, b und c zu erzeugen und c nicht lesbar zu machen:
touch
c; chmod 111 c; echo "Inhalt fuer a" > a; echo "und in b ebenso,
Blablabla...." > b
|
Bevor wir zu unserem eigentlichen Problem kommen, noch eine kleine
Vorübung. Ein Aufruf von "cat a b c d" liefert folgende Ausgabe:
$
cat a b c d
Inhalt fuer a
und in b ebenso, Blablabla....
cat: c: Permission denied
cat: d: No such file or directory
|
Es ist nicht schwer in diesem Beispiel zu erkennen, dass die beiden
ersten Ausgabenzeilen über die Standardausgabe und die beiden
letzten über den Fehlerkanal kommen.
In der folgenden Anweisung wird zunächst einmal die Standardausgabe auf die Datei (Pseudodatei) /dev/null umgeleitet. Dann wird mit der Anweisung 2>&1 der Deskriptor 2 (Fehlerkanal) auf die gleiche Ausgabedatei gesetzt wie der Deskriptor 1, d.h. auch dier Fehlermeldungen gehen jetzt auf /dev/null. (Für die, die es nicht wissen: /dev/null ist eine spezielle Pseudodatei, in der alle Eingaben
gewissermaßen "geerdet" werden, d.h. verschwinden)
cat
a b c d 1>/dev/null 2>&1 |
Man erhält keine Ausgaben auf dem Bildschirm, d.h. würde man
an obiges Kommando eine Pipe hängen, hätte der nachfolgende
Befehl eine leere Eingabe.
Was passiert nun, wenn wir "1> /dev/null" und "2>&1"
vertauschen?
$
cat a b c d 2>&1 1>/dev/null
cat: c: Permission denied
cat: d: No such file or directory
$ |
Zuerst wird in obigem Kommando der Deskriptor 2 (stderr) auf den gleichen Ausgabekanal wie der Deskriptor 1 gesetzt, d.h. stdout. Danach wird der Deskriptor 1 auf die "Datei" /dev/null gesetzt. Das bedeutet aber, dass die Fehler nun als Standardausgabe im Terminal ausgegeben werden, aber die Standardausgabe ist auf /dev/null gesetzt, d.h. man erhält keine Ausgaben sondern nur Fehlermeldungen.
Nun wollen wir eine Befehlsfolge konstruieren, die stderr und stdout vertauscht. Im Prinzip funktioniert es ähnlich wie in den meisten
Programmiersprachen,
wenn man die Werte zweier Variablen tauschen muss. In
Programmiersprachen braucht man meistens eine Hilfevariable und die
Vertauschung erfordert in einer Sequenz der folgenden Art:
Zum Vertauschen der Standardausgabe und der Standardeingabe braucht man
noch einen der anderen Filedeskriptoren von 3 bis 9. Normalerweise
werden sie nicht benutzt.Dieser wird als Hilfsdeskriptor benutzt und
den Wert von der Standardausgabe zwischenzuspeichern.
Im der folgenden Anweisung werden die Fehlerausgaben und die Ausgaben des Kommandos "command" vertauscht. Zuerst wir mit "3>&1" der Deskriptor 3 auf die Datei von Deskriptor 1 gesetzt, also stdout. Dann wird mit "1>&2" Der Deskriptor 1 auf den Wert des Deskriptors 2 gesetzt, also der Standardfehlerkanal. Nun muss man dem Deskriptor 2 noch den Wert der Standarardausgabe zuweisen, die wir im Deskriptor 3 gerettet hatten: "2>&3":
Die Ă„nderungen in den Deskriptoren in der obigen Anweisungen wollen wir uns im folgenden im Detail anschauen. Zu Beginn sieht es wie folgt aus:
Nach der Redirection 3>&1 enthält der Deskriptor 3 den Wert vom Deskriptor 1. Der Deskriptor 3 dient als Zwischenspeicher, um später mit "2>&3" den alten Wert von 1 im Deskriptor 2 zu speichern:
Nach der Redirection 1>&2 enthält der Deskriptor 1 den Wert vom Deskriptor 2:
Nach der letzten Redirection 2>&3 enthält der Deskriptor 2 den Wert vom Deskriptor 3, in dem der ursprüngliche Wert vom Deskriptor 1 gerettet worden ist:
Wie man im letzten Bild sieht, sind nun die Standardausgabe und die Standardfehlerausgabe getauscht worden.
Man kann dies mit folgendem Beispiel testen
$ cat a b c d 3>&1 1>&2 2>&3 | awk '{print "Input
vom awk: "$0}'
Inhalt fuer a
und in b ebenso, Blablabla....
Input vom awk: cat: c: Permission denied
Input vom awk: cat: d: No such file or directory
$
|
Wenn alle drei Dateien (Verzeichnisse) existieren, gibt es keine
Fehlermeldung und der AWK erhält infolgedessen keine Eingabe.
Existiert eine oder mehrere Dateien nicht oder ist der Zugriff nicht
gestattet, so werden diese Fehlermeldungen vom awk ausgegeben:
z.B.
Input vom awk: cat: c: Permission denied
Besonders ordentlich gestaltet man die Anweisung noch, wenn man den
Ausgabekanal 3 als letztes noch mit "3>&-" schließt.
cat
a b c d 3>&1 1>&2 2>&3 3>&- | awk '{print
"Input vom awk:
"$0}'
|
So macht das Ganze aber noch recht wenig Sinn. Ein etwas sinnvolleres
Beispiel einer Anwendung könnte der Fall sein, dass man die
Ausgabe eines Kommandos in einer Datei speichern will und die
Fehlermeldungen an einen in der Pipe nachgeschalteten Prozess zur
Weiterverarbeitung weiterleiten will. Ganz allgemein sieht das dann so
aus:
command
2> Ergebnisse 3>&1 1>&2 2>&3 3>&- | awk
'{print
"Input vom awk:
"$0}'
|
oder in Fortführung unseres vorigen Beipiels mit cat:
cat
a b c d 2> Ergebnisse 3>&1
1>&2 2>&3 3>&- | awk '{print
"Input vom awk:
"$0}'
|
|