GNU/Linux >> LINUX-Kenntnisse >  >> Linux

echo oder print /dev/stdin /dev/stdout /dev/stderr

stdin , stdout , und stderr sind Streams an die Dateideskriptoren 0, 1 bzw. 2 eines Prozesses angehängt.

An der Eingabeaufforderung einer interaktiven Shell in einem Terminal oder Terminalemulator würden alle diese 3 Dateideskriptoren auf dieselbe offene Dateibeschreibung verweisen, die durch Öffnen einer Terminal- oder Pseudo-Terminalgerätedatei erhalten worden wäre (etwas wie /dev/pts/0 ) im Lese-/Schreibmodus.

Wenn Sie von dieser interaktiven Shell aus Ihr Skript starten, ohne eine Umleitung zu verwenden, erbt Ihr Skript diese Dateideskriptoren.

Unter Linux /dev/stdin , /dev/stdout , /dev/stderr sind symbolische Links zu /proc/self/fd/0 , /proc/self/fd/1 , /proc/self/fd/2 jeweils selbst spezielle symbolische Links zu der eigentlichen Datei, die in diesen Dateideskriptoren geöffnet ist.

Sie sind nicht stdin, stdout, stderr, sondern spezielle Dateien, die angeben, zu welchen Dateien stdin, stdout, stderr gehören (beachten Sie, dass es in anderen Systemen als Linux, die diese speziellen Dateien haben, anders ist).

Etwas von stdin zu lesen bedeutet, von Dateideskriptor 0 zu lesen (der irgendwo innerhalb der Datei zeigt, auf die durch /dev/stdin verwiesen wird ).

Aber in $(</dev/stdin) , liest die Shell nicht von stdin, sie öffnet einen neuen Dateideskriptor zum Lesen in derselben Datei wie die, die in stdin geöffnet ist (es wird also vom Anfang der Datei gelesen, nicht dort, wo stdin derzeit zeigt).

Außer im Sonderfall von Endgeräten, die im Read+Write-Modus geöffnet sind, sind stdout und stderr normalerweise nicht zum Lesen geöffnet. Sie sind als Streams gedacht, in die Sie schreiben . Das Lesen aus dem Dateideskriptor 1 funktioniert also im Allgemeinen nicht. Öffnen Sie unter Linux /dev/stdout oder /dev/stderr zum Lesen (wie in $(</dev/stdout) ) würde funktionieren und Sie könnten aus der Datei lesen, wohin stdout geht (und wenn stdout eine Pipe wäre, würde das vom anderen Ende der Pipe lesen, und wenn es ein Socket wäre, würde es fehlschlagen, da Sie es nicht können öffnen eine Steckdose).

In unserem Fall, in dem das Skript ohne Umleitung an der Eingabeaufforderung einer interaktiven Shell in einem Terminal ausgeführt wird, sind alle /dev/stdin, /dev/stdout und /dev/stderr diese /dev/pts/x-Terminalgerätedatei.

Das Lesen aus diesen speziellen Dateien gibt zurück, was vom Terminal gesendet wird (was Sie auf der Tastatur eingeben). Wenn Sie an sie schreiben, wird der Text an das Terminal gesendet (zur Anzeige).

echo $(</dev/stdin)
echo $(</dev/stderr)

wird dasselbe sein. Um $(</dev/stdin) zu erweitern , öffnet die Shell dieses /dev/pts/0 und liest, was Sie eingeben, bis Sie ^D drücken auf einer leeren Zeile. Sie werden dann die Erweiterung (was Sie eingegeben haben, ohne die abschließenden Zeilenumbrüche und mit split+glob versehen) an echo übergeben die es dann auf stdout (zur Anzeige) ausgibt.

Allerdings in:

echo $(</dev/stdout)

in bash (und bash nur), ist es wichtig, dies innerhalb von $(...) zu erkennen , stdout wurde umgeleitet. Es ist jetzt ein Rohr. Im Fall von bash liest ein untergeordneter Shell-Prozess den Inhalt der Datei (hier /dev/stdout ) und schreibt es in die Pipe, während der Elternteil vom anderen Ende liest, um die Erweiterung zu bilden.

In diesem Fall öffnet dieser untergeordnete Bash-Prozess /dev/stdout , es öffnet tatsächlich das Leseende der Pfeife. Daraus wird nie etwas werden, es ist eine Sackgasse.

Wenn Sie aus der Datei lesen möchten, auf die die Skript-Stdout-Datei zeigt, würden Sie dies umgehen mit:

 { echo content of file on stdout: "$(</dev/fd/3)"; } 3<&1

Das würde fd 1 auf fd 3 duplizieren, also würde /dev/fd/3 auf dieselbe Datei wie /dev/stdout zeigen.

Mit einem Skript wie:

#! /bin/bash -
printf 'content of file on stdin: %s\n' "$(</dev/stdin)"
{ printf 'content of file on stdout: %s\n' "$(</dev/fd/3)"; } 3<&1
printf 'content of file on stderr: %s\n' "$(</dev/stderr)"

Bei Ausführung als:

echo bar > err
echo foo | myscript > out 2>> err

Sie würden in out sehen danach:

content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar

Im Gegensatz zum Lesen von /dev/stdin , /dev/stdout , /dev/stderr , Sie wollten von stdin, stdout und stderr lesen (was noch weniger Sinn machen würde), würden Sie Folgendes tun:

#! /bin/sh -
printf 'what I read from stdin: %s\n' "$(cat)"
{ printf 'what I read from stdout: %s\n' "$(cat <&3)"; } 3<&1
printf 'what I read from stderr: %s\n' "$(cat <&2)"

Wenn Sie das zweite Skript erneut gestartet haben als:

echo bar > err
echo foo | myscript > out 2>> err

Sie würden in out sehen :

what I read from stdin: foo
what I read from stdout:
what I read from stderr:

und in err :

bar
cat: -: Bad file descriptor
cat: -: Bad file descriptor

Für stdout und stderr cat schlägt fehl, weil die Dateideskriptoren zum Schreiben geöffnet waren nur, nicht lesend, die Erweiterung von $(cat <&3) und $(cat <&2) ist leer.

Wenn Sie es so genannt haben:

echo out > out
echo err > err
echo foo | myscript 1<> out 2<> err

(wobei <> öffnet sich im Lese-/Schreibmodus ohne Kürzung), würden Sie in out sehen :

what I read from stdin: foo
what I read from stdout:
what I read from stderr: err

und in err :

err

Sie werden feststellen, dass nichts von stdout gelesen wurde, weil der vorherige printf hatte den Inhalt von out überschrieben mit what I read from stdin: foo\n und verließ die stdout-Position innerhalb dieser Datei gleich danach. Wenn Sie out vorbereitet hatten mit etwas größerem Text, wie:

echo 'This is longer than "what I read from stdin": foo' > out

Dann kämen Sie auf out :

what I read from stdin: foo
read from stdin": foo
what I read from stdout: read from stdin": foo
what I read from stderr: err

Sehen Sie, wie der $(cat <&3) hat gelesen, was nach dem ersten printf übrig geblieben ist und bewegte damit auch die stdout-Position daran vorbei damit der nächste printf gibt aus, was danach gelesen wurde.


stdout und stderr sind Ausgänge, Sie können nicht von ihnen lesen, sondern nur darauf schreiben. Zum Beispiel:

echo "this is stdout" >/dev/stdout
echo "this is stderr" >/dev/stderr

Programme schreiben standardmäßig nach stdout, also ist der erste äquivalent zu

echo "this is stdout"

und Sie können stderr auf andere Weise umleiten, z. B.

echo "this is stderr" 1>&2

Linux
  1. So generieren Sie ein zufälliges Passwort unter Linux mit /dev/random

  2. Linux:Unterschied zwischen /dev/console , /dev/tty und /dev/tty0?

  3. Wie portabel sind /dev/stdin, /dev/stdout und /dev/stderr?

  4. So ordnen Sie /dev/sdX- und /dev/mapper/mpathY-Geräte vom /dev/dm-Z-Gerät zu

  5. Wie kann /dev/random oder /dev/urandom mit base64 codiert werden?

tty (/dev/tty ) vs. pts (/dev/pts) unter Linux

Wann sollte ich /dev/shm/ verwenden und wann sollte ich /tmp/?

DD von /dev/zero nach /dev/null ... was eigentlich passiert

Wie Linux /dev/tty und /dev/tty0 verwendet

Ist es falsch, /dev/random unter Linux mit /dev/urandom zu verknüpfen?

Warum sind < oder > erforderlich, um /dev/tcp