3>&4-
ist eine ksh93-Erweiterung, die auch von bash unterstützt wird, und das ist die Abkürzung für 3>&4 4>&-
: 3 zeigt jetzt auf die Stelle, an der 4 verwendet wurde, und 4 ist jetzt geschlossen.
Typische Verwendung ist in Fällen, in denen Sie eine Kopie dupliziert stdin
oder stdout
gespeichert haben und diese wiederherstellen möchten, wie in:
Angenommen, Sie möchten das stderr eines Befehls (und nur stderr) erfassen, während Sie stdout in einer Variablen alleine lassen.
Befehlsersetzung var=$(cmd)
, erzeugt eine Pipe. Das schreibende Ende der Pipe wird cmd
zu stdout (Dateideskriptor 1) und das andere Ende wird von der Shell gelesen, um die Variable zu füllen.
Nun, wenn Sie wollen , stderr
um die Variable gehen, könnten Sie tun: var=$(cmd 2>&1)
. Jetzt gehen sowohl fd 1 (stdout) als auch 2 (stderr) zur Pipe (und schließlich zur Variablen), was nur die Hälfte dessen ist, was wir wollen.
Wenn wir das tun var=$(cmd 2>&1-)
( Abkürzung für var=$(cmd 2>&1 >&-
), geht jetzt nur noch cmd
stderr zur Pipe, aber fd 1 ist geschlossen. Wenn cmd
versucht wird, eine Ausgabe zu schreiben, die mit einem EBADF
Fehler zurückkommt , wird beim Öffnen einer Datei die erste freie fd abgerufen und der geöffneten Datei zugewiesen, es sei stdout
denn, der Befehl schützt davor! Nicht das, was wir wollen.
Wenn wir wollen, dass die Standardressource cmd
in Ruhe gelassen wird, dh auf dieselbe Ressource verweist, auf die sie außerhalb der Befehlsersetzung zeigt, müssen wir diese Ressource irgendwie in die Befehlsersetzung einbinden. Dafür können wir eine Kopie von stdout
außerhalb der Befehlsersetzung erstellen, um sie in das Innere zu bringen.
{
var=$(cmd)
} 3>&1
Was ist eine sauberere Art zu schreiben:
exec 3>&1
var=$(cmd)
exec 3>&-
(was auch den Vorteil hat, fd 3 wiederherzustellen, anstatt es am Ende zu schließen).
Dann auf dem {
(oder den exec 3>&1
) und bis zu der }
, fd beide 1 und 3 auf die gleiche Ressource ursprünglich FD1 gezeigt wird. fd 3 zeigt auch auf diese Ressource innerhalb der Befehlsersetzung (die Befehlsersetzung leitet nur die fd 1, stdout um). Also oben haben cmd
wir für die FDS 1, 2, 3:
- das rohr zu var
- unberührt
- Das Gleiche wie das, was 1 außerhalb der Befehlsersetzung anzeigt
Wenn wir es ändern zu:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
Dann wird es:
- Das Gleiche wie das, was 1 außerhalb der Befehlsersetzung anzeigt
- das rohr zu var
- Das Gleiche wie das, was 1 außerhalb der Befehlsersetzung anzeigt
Jetzt haben wir, was wir wollten: stderr geht zur Pipe und stdout bleibt unberührt. Allerdings lecken wir diesen fd 3 zu cmd
.
Während Befehle (gemäß Konvention) davon ausgehen, dass die FDS 0 bis 2 geöffnet und Standardeingabe, -ausgabe und -fehler sind, nehmen sie nichts von anderen FDS an. Höchstwahrscheinlich werden sie diese fd 3 unberührt lassen. Wenn sie einen anderen Dateideskriptor benötigen, führen sie einfach einen aus, open()/dup()/socket()...
der den ersten verfügbaren Dateideskriptor zurückgibt. Wenn sie (wie ein Shell-Skript, das dies tut exec 3>&1
) fd
speziell verwenden müssen, weisen sie es zuerst zu (und in diesem Prozess wird die von unserem fd 3 gehaltene Ressource durch diesen Prozess freigegeben).
Es ist eine gute Praxis, das fd 3 zu schließen, da cmd
es nicht verwendet wird, aber es ist keine große Sache, wenn wir es zugewiesen lassen, bevor wir anrufen cmd
. Die Probleme können sein: dass cmd
(und möglicherweise andere Prozesse, die es hervorbringt) ein fd weniger zur Verfügung hat. Ein potenziell schwerwiegenderes Problem besteht darin, dass die Ressource, auf die fd verweist, möglicherweise von einem Prozess cmd
im Hintergrund gehalten wird. Es kann ein Problem sein, wenn diese Ressource eine Pipe oder ein anderer Kommunikationskanal zwischen Prozessen ist (wie wenn Ihr Skript ausgeführt wird als script_output=$(your-script)
), da dies bedeutet, dass der Prozess, der vom anderen Ende liest, bis dahin niemals das Dateiende sieht Hintergrundprozess wird beendet.
Also hier ist es besser zu schreiben:
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
Womit bash
kann gekürzt werden:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
Um die Gründe zusammenzufassen, warum es selten verwendet wird:
- Es ist kein Standard und nur syntaktischer Zucker. Sie müssen ein paar Tastenanschläge sparen, um Ihr Skript weniger portabel und für Leute, die an diese ungewöhnliche Funktion nicht gewöhnt sind, weniger offensichtlich zu machen.
- Die Notwendigkeit, den Original-FD nach dem Duplizieren zu schließen, wird oft übersehen, da wir die meiste Zeit nicht unter der Konsequenz leiden, also tun wir dies einfach
>&3
anstelle von >&3-
oder >&3 3>&-
.
Der Beweis, dass es nur selten verwendet wird, ist, wie Sie herausgefunden haben, dass es sich um einen Schwindel handelt . In Bash compound-command 3>&4-
oder any-builtin 3>&4-
Blätter fd 4 geschlossen, auch nachdem compound-command
oder any-builtin
zurückgekehrt ist. Ein Patch zur Behebung des Problems ist jetzt (19.02.2013) verfügbar.
{ var=$(cmd 2>&1 >&3) ; } 3>&1-
Ist es nicht ein Tippfehler beim Schließen von 1?$(...)
).{...}
zeigt fd 3 auf das, was fd 1 zum Zeigen verwendet hat, und fd 1 wird geschlossen. Beim Eintreten$(...)
wird fd 1 auf das Rohr gesetzt, das speist$var
, dann fürcmd
2 auf das Rohr und dann für 1 auf welche 3 Punkte zu, das ist die äußere 1. Die Tatsache, dass 1 danach geschlossen bleibt, ist ein Fehler in der Bash, ich werde es melden. ksh93, von dem diese Funktion stammt, hat diesen Fehler nicht.Dies bedeutet, dass auf dieselbe Stelle verwiesen wird, auf die der andere Dateideskriptor verweist. Sie müssen dies tun , sehr selten, abgesehen von der offensichtlichen getrennten Handhabung des Standardfehler Descriptor (
stderr
,fd 2
,/dev/stderr -> /proc/self/fd/2
). Es kann in einigen komplexen Fällen nützlich sein.Das Advanced Bash Scripting-Handbuch enthält das folgende Beispiel für eine längere Protokollebene und das folgende Snippet:
In Source Mage's Sorcery verwenden wir es zum Beispiel, um verschiedene Ausgaben desselben Codeblocks zu unterscheiden:
Aus Gründen der Protokollierung wurde eine zusätzliche Prozessersetzung hinzugefügt (VOYEUR entscheidet, ob die Daten auf dem Bildschirm angezeigt oder nur protokolliert werden sollen), es müssen jedoch immer einige Meldungen angezeigt werden. Um dies zu erreichen, drucken wir sie in den Dateideskriptor 3 und behandeln sie dann speziell.
quelle
In Unix werden Dateien von Dateideskriptoren behandelt (kleine Ganzzahlen, z. B. Standardeingabe ist 0, Standardausgabe ist 1, Standardfehler ist 2; wenn Sie andere Dateien öffnen, wird ihnen normalerweise der kleinste nicht verwendete Deskriptor zugewiesen). Wenn Sie also die Inards des Programms kennen und die Ausgabe, die an Dateideskriptor 5 gesendet wird, an die Standardausgabe senden möchten, verschieben Sie Deskriptor 5 nach 1. Daher kommen die
2> errors
und Konstruktionen,2>&1
in die Fehler dupliziert werden sollen der Ausgabestream.So wird es kaum benutzt (ich erinnere mich vage, dass ich es in meinen über 25 Jahren fast ausschließlicher Unix-Nutzung ein- oder zweimal im Zorn benutzt habe), aber wenn es gebraucht wird, ist es absolut notwendig.
quelle
5>&1
5" an "1" gesendet wird, was genau macht1>&5-
das dann neben dem Schließen von "5"?