103.4 Lektion 1
Zertifikat: |
LPIC-1 (101) |
---|---|
Version: |
5.0 |
Thema: |
103 GNU- und Unix-Befehle |
Lernziel: |
103.4 Ströme, Pipes und Umleitungen verwenden |
Lektion: |
1 von 2 |
Einführung
Alle Computerprogramme folgen dem gleichen allgemeinen Prinzip: Daten, die aus irgendeiner Quelle stammen, werden so transformiert, dass diese ein verständliches Ergebnis erzeugen. Im Linux-Shellkontext kann die Datenquelle eine lokale Datei, eine entfernte Datei, ein Gerät (wie eine Tastatur) usw. sein. Die Ausgabe des Programms wird normalerweise auf einem Bildschirm gerendert, aber es ist auch üblich, die Ausgabedaten in einem lokalen Dateisystem zu speichern, diese an ein entferntes Gerät zu senden oder über Audiolautsprecher abzuspielen usw.
Betriebssysteme wie Linux, die von Unix inspiriert sind, bieten eine große Vielfalt an Ein-/Ausgabemethoden. Insbesondere die Methode der Dateideskriptoren erlaubt es, Ganzzahlen dynamisch mit Datenkanälen zu verknüpfen, so dass ein Prozess sie als seine Ein-/Ausgabe-Datenströme referenzieren kann.
Bei üblichen Linuxprozessen sind standardmäßig drei Kommunikationskanäle geöffnet: der Standardeingabekanal (meist einfach stdin genannt), der Standardausgabekanal (stdout) und der Standardfehlerkanal (stderr). Die diesen Kanälen zugewiesenen numerischen Dateideskriptoren lauten 0
für stdin, 1
für stdout und 2
für stderr. Kommunikationskanäle sind auch über die speziellen Geräte /dev/stdin
, /dev/stdout
und /dev/stderr
zugänglich.
Diese drei Standardkommunikationskanäle ermöglichen es Programmierern, Code zu schreiben, der Daten liest und schreibt, ohne sich Gedanken über die Art der Medien zu machen, von denen sie kommen oder auf die sie gehen. Wenn ein Programm zum Beispiel einen Satz Daten als Eingabe benötigt, kann es einfach Daten von der Standardeingabe anfordern. Unabgängig davon was als Standardeingabe verwendet wird, wird es diese Daten liefern. Gleichermaßen ist die einfachste Methode, die ein Programm zur Anzeige seiner Ausgabe verwenden kann, diese in die Standardausgabe zu schreiben. In einer Standardshellsitzung wird die Tastatur als stdin und der Bildschirm als stdout und stderr definiert.
Die Bashshell hat die Fähigkeit, die Kommunikationskanäle beim Laden eines Programms neu zuzuweisen. Sie erlaubt es z.B., den Bildschirm als Standardausgabe zu überschreiben und eine Datei im lokalen Dateisystem als stdout zu verwenden.
Umleitungen
Die Neuzuweisung des Dateideskriptors eines Kanals in der Shellumgebung wird als Umleitung bezeichnet. Eine Umleitung wird durch ein spezielles Zeichen innerhalb der Befehlszeile definiert. Um z.B. die Standardausgabe eines Prozesses in eine Datei umzuleiten, wird das größer als Symbol >
am Ende des Befehls positioniert, gefolgt vom Pfad zu der Datei, welche die umgeleitete Ausgabe erhält:
$ cat /proc/cpuinfo >/tmp/cpu.txt
Standardmäßig werden nur die Inhalte, die nach stdout geschrieben werden, umgeleitet. Das geschieht, weil der numerische Wert des Dateideskriptors unmittelbar vor dem Größer-als-Zeichen angegeben wird. Falls er nicht angegeben wird, leitet die Bash die Standardausgabe um. Daher ist die Verwendung von >
gleichbedeutend mit der Verwendung von 1>
(der Wert des stdout Dateideskriptors entspricht 1
).
Um den Inhalt von stderr zu erfassen, sollte stattdessen die Umleitung 2>
verwendet werden. Die meisten Kommandozeilenprogramme senden Debuginformationen und Fehlermeldungen an den Standardfehlerkanal. So ist es beispielsweise möglich, die Fehlermeldung zu erfassen, die durch den Versuch, eine nicht existierende Datei zu lesen, ausgelöst wird:
$ cat /proc/cpu_info 2>/tmp/error.txt $ cat /tmp/error.txt cat: /proc/cpu_info: No such file or directory
Sowohl stdout als auch stderr werden mit &>
oder >&
auf dasselbe Ziel umgeleitet. Es ist wichtig, keine Leerzeichen neben das kaufmännische und-Zeichen zu setzen, sonst nimmt Bash es als Anweisung, den Prozess im Hintergrund laufen zu lassen und die Umleitung nicht durchzuführen.
Das Ziel muss ein Pfad zu einer beschreibbaren Datei, wie /tmp/cpu.txt
, oder ein beschreibbarer Dateideskriptor sein. Ein Dateideskriptorziel wird durch ein kaufmännisches und-Zeichen, gefolgt vom numerischen Wert des Dateideskriptors, dargestellt. Beispielsweise leitet 1>&2
stdout nach stderr um. Um das Gegenteil zu erreichen, stderr nach stdout umzuleiten, sollte stattdessen 2>&1
verwendet werden.
Obwohl es nicht sehr nützlich ist, da es einen kürzeren Weg gibt, die gleiche Aufgabe zu erledigen, ist es möglich, stderr nach stdout umzuleiten und es dann in eine Datei umzuleiten. Zum Beispiel kann eine Umleitung, bei der sowohl stderr als auch stdout in eine Datei namens log.txt
geschrieben wird, als >log.txt 2>&1
geschrieben werden. Der Hauptgrund für die Umleitung von stderr nach stdout ist jedoch das Parsen von Debug- und Fehlermeldungen zu ermöglichen. Es ist möglich, die Standardausgabe eines Programms auf die Standardeingabe eines anderen Programms umzuleiten, aber es ist nicht möglich, den Standardfehler direkt auf die Standardeingabe eines anderen Programms umzuleiten. Daher müssen die an stderr gesendeten Nachrichten eines Programms zunächst nach stdout umgeleitet werden, damit diese von stdin eines anderen Programms gelesen werden können.
Um die Ausgabe eines Befehls einfach zu verwerfen, kann sein Inhalt in die spezielle Datei /dev/null
umgeleitet werden. Zum Beispiel speichert >log.txt 2>/dev/null
den Inhalt von stdout in der Datei log.txt
und verwirft den stderr. Die Datei /dev/null
kann von jedem Benutzer beschrieben werden, aber es können keine Daten aus ihr wiederhergestellt werden, da diese nirgendwo gespeichert werden.
Eine Fehlermeldung wird angezeigt, wenn das angegebene Ziel nicht beschreibbar ist (wenn der Pfad auf ein Verzeichnis oder eine schreibgeschützte Datei zeigt) und keine Änderungen am Ziel vorgenommen werden können. Eine Ausgabeumleitung überschreibt jedoch ein vorhandenes beschreibbares Ziel ohne jegliche Nachfrage. Dateien werden durch Ausgabeumleitungen überschrieben, es sei denn, die Bashoption noclobber
ist aktiviert, was für die aktuelle Sitzung mit dem Befehl set -o noclobber
oder set -C
geschehen kann:
$ set -o noclobber $ cat /proc/cpu_info 2>/tmp/error.txt -bash: /tmp/error.txt: cannot overwrite existing file
Um die Option noclobber
für die aktuelle Sitzung zurückzusetzen, führen Sie set +o noclobber
oder set +C
aus. Um die Option noclobber
persistent zu machen, muss sie in das Bashprofil des Benutzers oder in das systemweite Profil aufgenommen werden.
Selbst bei aktivierter Option noclobber
ist es möglich, umgeleitete Daten an bestehende Inhalte anzuhängen. Dies wird durch eine Umleitung erreicht, welche mit zwei Größer-als-Zeichen >>
notiert wird:
$ cat /proc/cpu_info 2>>/tmp/error.txt $ cat /tmp/error.txt cat: /proc/cpu_info: No such file or directory cat: /proc/cpu_info: No such file or directory
Im vorherigen Beispiel wurde die neue Fehlermeldung an die bestehende Fehlermeldung in der Datei /tmp/error.txt
angehängt. Wenn die Datei noch nicht existiert, wird diese mit den neuen Daten erstellt.
Die Datenquelle der Standardeingabe eines Prozesses kann ebenfalls neu zugeordnet werden. Das Symbol <
wird verwendet, um den Inhalt einer Datei auf den stdin eines Prozesses umzuleiten. In diesem Fall fließen die Daten von rechts nach links: der neu zugewiesene Deskriptor wird links vom Kleiner-als-Zeichen als 0 angenommen, und die Datenquelle (ein Pfad zu einer Datei) muss rechts vom Kleiner-als-Zeichen stehen. Der Befehl uniq
akzeptiert, wie die meisten Befehlszeilendienstprogramme zur Textverarbeitung, standardmäßig an stdin gesendete Daten:
$ uniq -c </tmp/error.txt 2 cat: /proc/cpu_info: No such file or directory
Die Option -c
lässt uniq
anzeigen, wie oft eine sich wiederholende Zeile im Text erscheint. Da der numerische Wert des umgeleiteten Dateideskriptors unterdrückt wurde, ist der Beispielbefehl äquivalent zu uniq -c 0</tmp/error.txt
. Die Verwendung eines anderen Dateideskriptors als 0
in einer Eingabeumleitung ist nur in bestimmten Kontexten sinnvoll, da es für ein Programm möglich ist, Daten bei den Dateideskriptoren 3
, 4
usw. abzufragen. Tatsächlich können Programme jede Ganzzahl über 2 als neue Dateideskriptoren für die Datenein- und -ausgabe verwenden. Zum Beispiel liest der folgende C-Code Daten aus dem Dateideskriptor 3
und repliziert diese einfach in den Dateideskriptor 4
:
Note
|
Das Programm muss solche Dateideskriptoren korrekt behandeln, da es sonst einen ungültigen Lese- oder Schreibvorgang verursachen würde und abstürzen könnte. |
#include <stdio.h> int main(int argc, char **argv){ FILE *fd_3, *fd_4; // Open file descriptor 3 fd_3 = fdopen(3, "r"); // Open file descriptor 4 fd_4 = fdopen(4, "w"); // Read from file descriptor 3 char buf[32]; while ( fgets(buf, 32, fd_3) != NULL ){ // Write to file descriptor 4 fprintf(fd_4, "%s", buf); } // Close both file descriptors fclose(fd_3); fclose(fd_4); }
Um das Programm zu testen, speichern Sie den Beispielcode als fd.c
und kompilieren Sie ihn mittels gcc -o fd fd.c
. Dieses Programm benötigt die Dateideskriptoren 3 und 4, damit es sie lesen und schreiben kann. Als Beispiel kann die zuvor erstellte Datei /tmp/error.txt
als Quelle für den Dateideskriptor 3
verwendet werden und der Dateideskriptor 4
kann nach stdout umgeleitet werden:
$ ./fd 3</tmp/error.txt 4>&1 cat: /proc/cpu_info: No such file or directory cat: /proc/cpu_info: No such file or directory
Aus der Sicht eines Programmierers vermeidet die Verwendung von Dateideskriptoren sich mit Optionsparsing und Dateisystempfaden befassen zu müssen. Derselbe Dateideskriptor kann sogar als Ein- und Ausgabe verwendet werden. In diesem Fall wird der Dateideskriptor in der Befehlszeile sowohl mit dem Kleiner-als- sowie mit dem Größer-als-Zeichen definiert, wie in 3<>/tmp/error.txt
.
Here-Dokument und Here-String
Eine weitere Möglichkeit Eingaben umzuleiten, sind die Methoden Here-Dokument und Here-String. Die Here-Dokumentumleitung ermöglicht die Eingabe von mehrzeiligem Text, der als umgeleiteter Inhalt verwendet wird. Zwei Kleiner-als-Zeichen <<
kennzeichnen eine Here-Dokumentweiterleitung:
$ wc -c <<EOF > How many characters > in this Here document? > EOF 43
Rechts der beiden Kleiner-als-Zeichen <<
steht der Endbegriff EOF
. Der Einfügemodus wird beendet, sobald eine Zeile eingegeben wird, die nur den Endbegriff enthält. Jeder andere Begriff kann als Endbegriff verwendet werden, aber es ist wichtig zwischen dem Kleiner-als-Zeichen und dem Endbegriff keine Leerzeichen einzufügen. Im obigen Beispiel wurden die beiden Textzeilen an den stdin des Befehls wc -c
gesendet, der die Anzahl der übergebenen Zeichen anzeigt. Wie bei der Eingabeumleitung für Dateien wird der stdin (Dateideskriptor 0
) angenommen, wenn der umgeleitete Dateideskriptor nicht angegeben wird.
Die Here-Stringmethode ist der Here-Dokumentmethode sehr ähnlich, jedoch nur für eine Zeile bestimmt:
$ wc -c <<<"How many characters in this Here string?" 41
In diesem Beispiel wird die Zeichenfolge rechts von den drei Kleiner-als-Zeichen an stdin von wc -c
gesendet, der die Anzahl der Zeichen ausgibt. Zeichenketten, die Leerzeichen enthalten, müssen in Anführungszeichen stehen, andernfalls wird nur das erste Wort als Here-String verwendet und die restlichen werden als Argumente an den Befehl übergeben.
Geführte Übungen
-
Zusätzlich zu Textdateien kann der Befehl
cat
auch mit binären Daten arbeiten, wie z.B. den Inhalt eines Blockgerätes in eine Datei senden. Wie kanncat
mittels Umleitung den Inhalt vom Gerät/dev/sdc
an die Dateisdc.img
im aktuellen Verzeichnis senden? -
Wie lautet der Name des Standardkanals, der durch den Befehl
date 1> now.txt
umgeleitet wird? -
Nach dem Versuch eine Datei mit Hilfe der Umleitung zu überschreiben, erhält ein Benutzer eine Fehlermeldung mit der Information, dass die Option
noclobber
aktiviert ist. Wie kann die Optionnoclobber
für die aktuelle Sitzung deaktiviert werden? -
Wie lautet das Ergebnis des Befehls
cat <<.>/dev/stdout
?
Offene Übungen
-
Der Befehl
cat /proc/cpu_info
zeigt eine Fehlermeldung, weil/proc/cpu_info
nicht vorhanden ist. Wohin leitet der Befehlcat /proc/cpu_info 2>1
die Fehlermeldung um? -
Wird es immer noch möglich sein, an
/dev/null
gesendete Inhalte zu verwerfen, wenn die Optionnoclobber
für die aktuelle Shellsitzung aktiviert ist? -
Wie könnte der Inhalt der Variable
$USER
ohne die Verwendung vonecho
auf stdin des Befehlssha1sum
umgeleitet werden? -
Der Linux-Kernel behält symbolische Links in
/proc/PID/fd/
zu jeder Datei, die von einem Prozess geöffnet ist, wobei PID die Identifikationsnummer des entsprechenden Prozesses darstellt. Wie könnte der Systemadministrator dieses Verzeichnis benutzen, um den Ort der vonnginx
geöffneten Protokolldateien zu überprüfen, angenommen seine PID lautet1234
? -
Es ist möglich, arithmetische Berechnungen nur mit den in der Shell eingebauten Befehlen durchzuführen, aber Fließkommaberechnungen erfordern spezielle Programme wie
bc
(basic calculator). Mitbc
ist es sogar möglich durch den Parameterscale
die Anzahl der Nachkommastellen zu definieren. Allerdings akzeptiertbc
Operationen nur über seine Standardeingabe, die normalerweise im interaktiven Modus eingegeben wird. Wie kann die Fließkommaoperationscale=6; 1/3
unter Verwendung eines Here-Strings an die Standardeingabe vonbc
geschickt werden?
Zusammenfassung
Diese Lektion behandelt Methoden zur Ausführung eines Programms, das seine Standardkommunikationskanäle umleitet. Linuxprozesse verwenden diese Standardkanäle als generische Dateideskriptoren zum Lesen und Schreiben von Daten, wodurch es möglich wird diese beliebig in Dateien oder Geräte zu schreiben. Die Lektion beinhaltet die folgenden Punkte:
-
Was Dateideskriptoren sind und welche Rolle sie in Linux spielen.
-
Die Standardkommunikationskanäle jedes Prozesses: stdin, stdout und stderr.
-
Wie man einen Befehl mit Datenumleitung korrekt ausführt, sowohl für die Eingabe als auch für die Ausgabe.
-
Wie man Here-Dokumente und Hier-Strings in Eingabeumleitungen verwendet.
Die behandelten Befehle und Verfahren lauten:
-
Umleitungsoperatoren:
>
,<
,>>
,<<
,<<<
. -
Die Befehle
cat
,set
,uniq
undwc
.
Lösungen zu den geführten Übungen
-
Zusätzlich zu Textdateien kann der Befehl
cat
auch mit binären Daten arbeiten, wie z.B. den Inhalt eines Blockgerätes in eine Datei senden. Wie kanncat
mittels Umleitung den Inhalt vom Gerät/dev/sdc
an die Dateisdc.img
im aktuellen Verzeichnis senden?$ cat /dev/sdc > sdc.img
-
Wie lautet der Name des Standardkanals, der durch den Befehl
date 1> now.txt
umgeleitet wird?Standardausgabe oder stdout
-
Nach dem Versuch eine Datei mit Hilfe der Umleitung zu überschreiben, erhält ein Benutzer eine Fehlermeldung mit der Information, dass die Option
noclobber
aktiviert ist. Wie kann die Optionnoclobber
für die aktuelle Sitzung deaktiviert werden?set +C
oderset +o noclobber
-
Wie lautet das Ergebnis des Befehls
cat <<.>/dev/stdout
?Bash geht in den Heredoc-Eingabemodus und verlässt diesen, sobald ein Punkt in einer Zeile erscheint. Der eingegebene Text wird anschließend nach stdout umgeleitet (auf dem Bildschirm angezeigt).
Lösungen zu den offenen Übungen
-
Der Befehl
cat /proc/cpu_info
zeigt eine Fehlermeldung, weil/proc/cpu_info
nicht vorhanden ist. Wohin leitet der Befehlcat /proc/cpu_info 2>1
die Fehlermeldung um?Zu einer Datei namens
1
im aktuellen Verzeichnis. -
Wird es immer noch möglich sein, an
/dev/null
gesendete Inhalte zu verwerfen, wenn die Optionnoclobber
für die aktuelle Shellsitzung aktiviert ist?Ja,
/dev/null
ist eine spezielle Datei, die nicht vonnoclobber
betroffen ist. -
Wie könnte der Inhalt der Variable
$USER
ohne die Verwendung vonecho
auf stdin des Befehlssha1sum
umgeleitet werden?$ sha1sum <<<$USER
-
Der Linux-Kernel behält symbolische Links in
/proc/PID/fd/
zu jeder Datei, die von einem Prozess geöffnet ist, wobei PID die Identifikationsnummer des entsprechenden Prozesses darstellt. Wie könnte der Systemadministrator dieses Verzeichnis benutzen, um den Ort der vonnginx
geöffneten Protokolldateien zu überprüfen, angenommen seine PID lautet1234
?Durch den Befehl
ls -l /proc/1234/fd
, der die Ziele jedes symbolischen Links im Verzeichnis anzeigt. -
Es ist möglich, arithmetische Berechnungen nur mit den in der Shell eingebauten Befehlen durchzuführen, aber Fließkommaberechnungen erfordern spezielle Programme wie
bc
(basic calculator). Mitbc
ist es sogar möglich durch den Parameterscale
die Anzahl der Nachkommastellen zu definieren. Allerdings akzeptiertbc
Operationen nur über seine Standardeingabe, die normalerweise im interaktiven Modus eingegeben wird. Wie kann die Fließkommaoperationscale=6; 1/3
unter Verwendung eines Here-Strings an die Standardeingabe vonbc
geschickt werden?$ bc <<<"scale=6; 1/3"