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.