Shells: Tutorial


* Einführung * Installation * Paketmanager * Grundlagen * Shells * Entwicklung * KDE * Skriptsprachen * AWK * PHP * Perl * Apache * Veranstaltungen * Schulungen * Bücher * Netzwerk * OpenOffice * OpenSource * Samba

Shells


© <a href="http://www.fotolia.de/p/29003">Ljupco Smokovski</a> - FOTOLIA
Home
Erste Schritte mit der Bourne-Shell
Rechtschreibung
Schleifen und Zählen
Schleifen und elegant Zählen
Pipes
Wer sucht der findet!
Head or Tail
Praktische Shell-Skripte
Verknüpfen von Textdateien mittels Schlüsselfeldern
Klammer-Erweiterung
Shell-Variablen
Sortieren von Daten
Dateien zeilenweise zusammenfügen
Texte formatieren mit fmt und fold
Umlenkung der Ein- und der Ausgaben
Mit freundlicher Unterstützung von:

Linux-Kurse und Seminare Linux Kurse
Veranstalter des
Linux-Tag
am Bodensee
2007 und 2008

Kontakt
Haftung
Impressum
Problem Hilfe Startseite


Ein- und Ausgabeumlenkung

UmleitungIn 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
Standardeingabe stdin Tastatur
Standardausgabe stdout Bildschirm
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):

wc -l < test.txt

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

wc -l  test.txt

aufrufen können

Für die Ausgabeumlenkung benutzt man das Größerzeichen, also ">":

ls > dir.txt

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 ">>".

ls >> dir.txt

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:

help = a;
a=b;
b=help;

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":

command 3>&1 1>&2 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:
Zustände der Deskriptoren bei der Umlenkung
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:
Zustände der Deskriptoren bei der Umlenkung
Nach der Redirection 1>&2 enthält der Deskriptor 1 den Wert vom Deskriptor 2:
Zustände der Deskriptoren bei der Umlenkung
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:
Zustände der Deskriptoren bei der Umlenkung
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}'


© Copyright 2007 - 2010, Bernd Klein mit freundlicher Unterstützung von Bodenseo, Linux-Kurse und Schulungen,
Foto linke Seite (Mann mit Strick und Colt): Foto: © Ljupco Smokovski, fotolia 984022