Ist dies ein Tippfehler im Umleitungsabschnitt des Bash-Handbuchs?

13
Note that the order of redirections is significant.  For example, the command

          ls > dirlist 2>&1

   directs both standard output and standard error to the file dirlist, 
   while the command

          ls 2>&1 > dirlist

   directs  only  the  standard  output  to  file  dirlist,  because the 
   standard error was duplicated from the standard output before the standard
   output was redirected to dirlist.

Nun, dieser letzte Teil verwirrt mich. In diesem Fall würde ein Standardfehler auf dem Terminal ausgegeben und ein STDOUT in die Dirlistendatei verschoben. Das würde passieren, aber so verstehe ich das Handbuch nicht.

Es scheint, als sollte es heißen "weil der Standardfehler von der Standardausgabe dupliziert wurde, NACHDEM die Standardausgabe zur Verzeichnisliste umgeleitet wurde". Wenn STDERR an STDOUT gesendet wurde, bevor STDOUT an eine Datei weitergeleitet wurde, enthält die Datei dann nicht STDOUT AND STDERR?

Kann das bitte jemand für mich klären? Ist es nur ein schlechtes Leseverständnis von meiner Seite? Die Verwendung des Wortes Vervielfältigung erscheint mir in diesem Zusammenhang etwas seltsam. Vielleicht wirft mich das.

Gregg Leventhal
quelle
1
Ein klassischer Fall der Verwechslung von Operationen, die "nach Wert" oder "nach Referenz" sind. Wenn Sie einen Dateideskriptor duplizieren, handelt es sich um eine By-Value- Operation. In der Programmierung, nachdem a = 1; b = a; a = 2Sie erwarten a == 2 && b == 1, wahr zu sein. Die Umleitung 2>&1ähnelt der b = aZuweisung - es handelt sich um einen Wert, nicht um einen Verweis. 2>&1heiratet Dateideskriptor 2 nicht für alle Ewigkeit mit Dateideskriptor 1 - es sind immer noch 2 verschiedene Dateideskriptoren, die zufällig auf dieselbe Datei verweisen.
JW013

Antworten:

23

Vervielfältigung ist hier wirklich der wichtige Teil.

Mal sehen, wohin die Dateideskriptoren vor der Umleitung gehen. Dies ist normalerweise das aktuelle Terminal, zB:

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

Wenn wir jetzt ls -lohne Umleitung anrufen , gehen Ausgabe- und Fehlermeldungen zu meinem Endgerät unter /dev/pts/1.

Wenn wir das zuerst STDOUTin eine Datei umleiten ( ls -l > dirlist), sieht es so aus:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Wenn wir dannSTDERR zu einem Duplikat von STDOUT's file descriptor ( ls -l > dirlist 2>&1) umleiten , gehen wir STDERRzu einem Duplikat von /home/bon/dirlist:

STDOUT ---> /home/bon/dirlist
STDERR ---> /home/bon/dirlist

Wenn wir zuerstSTDERR zu einem Duplikat von STDOUT's file descriptor ( ls -l 2>&1) umleiten würden :

STDOUT ---> /dev/pts/1
STDERR ---> /dev/pts/1

und dann STDOUT zu einer Datei ( ls -l 2>&1 > dirlist), würden wir dies bekommen:

STDOUT ---> /home/bon/dirlist
STDERR ---> /dev/pts/1

Hier geht STDERRes noch zum Terminal.

Sie sehen, die Reihenfolge in der Manpage ist korrekt.


Umleitung testen

Jetzt können Sie das selbst testen. Mit sehen ls -l /proc/$$/fd/Sie, wo STDOUT(mit fd 1) und STDERR(mit fd 2) der aktuelle Prozess abläuft:

$ ls -l /proc/$$/fd/
total 0
lrwx------ 1 bon bon 64 Jul 24 18:19 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 07:41 2 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 18:19 255 -> /dev/pts/1

Lassen Sie uns ein kleines Shell-Skript erstellen, das zeigt, wohin Ihre Dateideskriptoren zeigen. Auf diese Weise erhalten wir beim Aufrufen immer den Status ls, einschließlich der Umleitung von der aufrufenden Shell.

$ cat > lookfd.sh
#!/bin/sh
ls -l /proc/$$/fd/
^D
$ chmod +x lookfd.sh

(Mit CtrlDsenden Sie ein Dateiende und beenden so das catLesen des Befehls von STDIN.)

Rufen Sie nun dieses Skript mit verschiedenen Kombinationen von Umleitungen auf:

$ ./lookfd.sh 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:08 0 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:08 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:08 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh 2>&1 > foo.out
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:10 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:10 1 -> /home/bon/foo.out
lrwx------ 1 bon bon 64 Jul 24 19:10 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:10 255 -> /home/bon/lookfd.sh
$ ./lookfd.sh > foo.out 2>&1
$ cat foo.out 
total 0
lrwx------ 1 bon bon 64 Jul 24 19:11 0 -> /dev/pts/1
l-wx------ 1 bon bon 64 Jul 24 19:11 1 -> /home/bon/foo.out
l-wx------ 1 bon bon 64 Jul 24 19:11 2 -> /home/bon/foo.out
lr-x------ 1 bon bon 64 Jul 24 19:11 255 -> /home/bon/lookfd.sh

Sie sehen, dass die Dateideskriptoren 1 (für STDOUT) und 2 (für STDERR) variieren. Zum Spaß könnten Sie auch umleiten STDINund das Ergebnis sehen:

$ ./lookfd.sh < /dev/zero
total 0
lr-x------ 1 bon bon 64 Jul 24 19:18 0 -> /dev/zero
lrwx------ 1 bon bon 64 Jul 24 19:18 1 -> /dev/pts/1
lrwx------ 1 bon bon 64 Jul 24 19:18 2 -> /dev/pts/1
lr-x------ 1 bon bon 64 Jul 24 19:18 255 -> /home/bon/lookfd.sh

(Frage an den Leser: Wo steht der Dateideskriptor 255? ;-))

Dubu
quelle
+1 - ausgezeichnete Antwort. Sehr gut geschriebene und großartige Beispiele. Vielen Dank!!!
SLM
Ich glaube, mein Missverständnis war, dass die Umleitung für alle folgenden Befehle persistent war, so dass alle STDERR für den Rest der Zeile an STDOUT gingen.
Gregg Leventhal
2

Nein, das Handbuch ist richtig.

Wenn 1 zuerst auf das Terminal und 2 auch auf das Terminal zeigt, dann:

command  2>&1   1>somewhere

Die Auswertung der Umleitung erfolgt von links nach rechts.

Es wird also ZUERST ausgewertet 2>&1und kopiert, worauf fd zeigte 1(dh den Dateideskriptor von the terminalnormalerweise / dev / tty), in fd2 .

An diesem Punkt zeigt fd 2nun auf die Stelle , auf die fd 1früher zeigte (the terminal ) zeigte.

Und DANN wertet es den 1>somewhereTeil aus und kopiert damit den Dateideskriptor von somewherein fd 1(an diesem Punkt zeigt fd 1nun auf somewhereund fd zeigt 2immer noch auf the terminal).

Es wird also tatsächlich 1 in "irgendwo" und 2 in das Terminal gedruckt, da 2 von 1 dupliziert wurde, BEVOR 1 geändert wurde.

Die andere Reihenfolge:

command  1>somewhere 2>&1

Leitet zuerst fd 1nach um somewhereund kopiert dann die gleiche Referenz in fd 2, so dass am Ende 2 auch auf zeigt somewhere. Sie sind aber von nun an nicht mehr "verknüpft". Jeder kann weiterhin separat umgeleitet werden.

Ex:

command  1>somewhere 2>&1
exec 2>/dev/null

Am Ende davon zeigt fd 1auf somewhereund fd 2ist auf gerichtet/dev/null

Übliche Namen für fd 1sind STDOUT (Standardausgabe), und der übliche Name für fd 2ist STDERR (Standardfehler, da er häufig zur Anzeige von Fehlern ohne Beeinträchtigung von STDOUT verwendet wird).

Olivier Dulac
quelle
@ Michael-mrozek: danke für den edit, aber ich bestehe darauf "copy" anstatt "duplicate" zu sagen, da "duplicate" einen dazu bringen könnte zu glauben, dass von nun an beide "das gleiche" sind, was nicht mehr stimmt. Bsp . cmd 1>somewhere 2>&1 ; exec 2>/dev/null: Nach der Ausführung wurden nur 2 nach / dev / null umgeleitet (1 geht immer noch nach "irgendwo"). Ich brauche Hilfe, um einen Weg zu finden, um zu sagen, "was 1 Punkte" statt "der fd 1", aber ... da das auch verwirrend ist ...
Olivier Dulac
1
Ich bin mir nicht sicher was du meinst; du bist derjenige, der es von "kopieren" auf "duplizieren" geändert hat. Alles, was ich getan habe, war, Dinge groß zu schreiben und zu formatieren, ich habe kein Wort geändert
Michael Mrozek
doh ... ^^ sorry. Und ich redigierte noch einmal, um neu zu formulieren, um genauer zu machen, was in was kopiert wird ^^
Olivier Dulac
1

Ich denke, der verwirrende Teil hier ist das Missverständnis, dass die Umleitung von stderr zu stdout die beiden Streams tatsächlich verbindet.

Eine durchaus vernünftige Idee, aber was passiert, wenn Sie schreiben, 2>&1ist, dass stderr einen Blick auf das wirft, was stdout schreibt, und an denselben Ort selbst schreibt. Wenn Sie stdout anschließend anweisen, an eine andere Stelle zu schreiben, hat dies keine Auswirkung auf das Ziel von stderr, das bereits verschoben wurde.

Ich denke, es ist ein bisschen eingängig, aber so funktioniert es. Stellen Sie ein, wo Sie zuerst schreiben möchten, und sagen Sie dann allen, dass Sie mich kopieren möchten. Hoffe das klärt ...

jrichemont
quelle
0

VERVIELFÄLTIGUNG...

ist wichtig, aber eher in dem Sinne, dass es die Quelle vieler Verwirrung ist . Das ist ganz einfach. Diese Antwort ist nur eine "radikale" Illustration.

Die akzeptierte Antwort ist gut, aber zu lang und betont "Duplikation".

Das Q endet weise mit:

Die Verwendung des Wortes Vervielfältigung erscheint mir in diesem Zusammenhang etwas seltsam. Vielleicht wirft mich das.

Ich benutze die Bash-Notation und definiere die Variablen "eins" und "zwei" als Dateihandles "1" und "2". Der (Ausgabe-) Umleitungsoperator >ist eine Zuweisung =. &und $Mittelwert von.

Die man bash Beispiele (mit Standard "1" hinzugefügt)

ls 1>dirlist 2>&1      # both to dirlist
ls 2>&1 1>dirlist      # 1 to dirlist, 2 stays on tty/screen 

werden:

one=dirlist  two=$one

und

two=$one   one=dirlist

Und selbst das ist für mich und einige andere, denke ich, nicht automatisch. In der ersten Zeile werden Sie mit $oneund $twobeide mit "dirlist" belassen. Natürlich.

Die zweite Zeile beginnt mit einer unnützen Zuweisung. Beide beginnen per Definition mit "TTY" (ein bisschen symbolisch) als Richtung ; Durch diese Zuweisung wird kein Wert geändert, und bei Variablen wie bei Dateihandles ist nichts magisch verknüpft. Die Variable twowird von den folgenden Punkten nicht beeinflusst one=dirlist. Natürlich nicht.

Jemand hier (vor 6 Jahren) hat "point to" anstelle von "copy" oder "duplicate" vorgeschlagen und dann festgestellt: Das wäre auch verwirrend.

Diese Duplikation oder Zeigersemantik wird nicht einmal benötigt. Vielleicht ist es das kaufmännische Und, das mehr Aufmerksamkeit benötigt. Der "Wert von" Operator / Token / was auch immer.

Wenn - und nur wenn - Sie nach einer Möglichkeit suchen, eine überraschende Auftragsnummer auf Ihrer Konsole zu erhalten , dann eine Meldung "erledigt" und als Bonus eine Datei mit dem Namen "2", dann gehen Sie folgendermaßen vor:

ls 1>2& 2>/dev/null

Es liest sich natürlich als " copy" / "duplicate" 1 zu 2 und dann beide zusammen auf null . Aber die Idee ist falsch und auch die Syntax. (aber kein Syntaxfehler, es ist gültig)

Der richtige Weg, dies zu planen, besteht darin, eine der beiden auf null umzuleiten und dann die ANDERE an dieselbe Stelle umzuleiten:

ls 1>/dev/null 2>&1
# or 
ls 2>/dev/null 1>&2

(die führende "1" kann weggelassen werden)

(OK das acc. A ist nicht zu lang, aber es ist zu viel von einer Liste - oder: sehr gute Visualisierung, nicht so gute Erklärung)

Rastafile
quelle