Wenn Sie eine Befehlsliste umleiten, die eine Exec-Umleitung enthält, scheint das exec> / dev / null danach nicht mehr angewendet zu werden, wie zum Beispiel mit:
{ exec >/dev/null; } >/dev/null; echo "Hi"
"Hi" wird gedruckt.
Ich hatte den Eindruck, dass die {}
Befehlsliste nur dann als Subshell betrachtet wird, wenn sie Teil einer Pipeline ist. Daher sollte die Befehlsliste exec >/dev/null
in meiner Vorstellung immer noch in der aktuellen Shell-Umgebung angewendet werden.
Nun, wenn Sie es ändern:
{ exec >/dev/null; } 2>/dev/null; echo "Hi"
es erfolgt keine erwartete Ausgabe; Dateideskriptor 1 zeigt auch für zukünftige Befehle auf / dev / null. Dies wird durch erneutes Ausführen gezeigt:
{ exec >/dev/null; } >/dev/null; echo "Hi"
das wird keine Ausgabe geben.
Ich habe versucht, ein Skript zu erstellen und es zu zeichnen, bin mir aber immer noch nicht sicher, was genau hier passiert.
Was passiert an jedem Punkt in diesem Skript mit dem STDOUT-Dateideskriptor?
BEARBEITEN: Hinzufügen meiner Strace-Ausgabe:
read(255, "#!/usr/bin/env bash\n{ exec 1>/de"..., 65) = 65
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
close(10) = 0
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
fstat(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
ioctl(1, TCGETS, 0x7ffee027ef90) = -1 ENOTTY (Inappropriate ioctl for device)
write(1, "hi\n", 3) = 3
quelle
close(10)
. Können Sie auch Ihren gesamten Skriptinhalt posten, auf den Sie gerade zugegangen sind?;
danach}
, der die Bedeutung von ändert,> /dev/null
um schließlich nicht auf die Verbundliste anzuwenden{}
.Antworten:
Lasst uns folgen
Schritt für Schritt.
Es gibt zwei Befehle:
ein.
{ exec >/dev/null; } >/dev/null
, gefolgt vonb.
echo "Hi"
Die Shell führt zuerst den Befehl (a) und dann den Befehl (b) aus.
Die Ausführung
{ exec >/dev/null; } >/dev/null
erfolgt wie folgt:ein. Zuerst führt die Shell die Umleitung durch
>/dev/null
und merkt sich, um sie rückgängig zu machen, wenn der Befehl endet .b. Dann wird die Shell ausgeführt
{ exec >/dev/null; }
.c. Schließlich schaltet die Shell die Standardausgabe wieder auf den ursprünglichen Zustand zurück. (Dies ist derselbe Mechanismus wie bei
ls -lR /usr/share/fonts >~/FontList.txt
Umleitungen, die nur für die Dauer des Befehls ausgeführt werden, zu dem sie gehören.)Sobald der erste Befehl ausgeführt wurde, wird die Shell ausgeführt
echo "Hi"
. Die Standardausgabe befindet sich dort, wo sie vor dem ersten Befehl war.quelle
Um nicht eine Unterschale bzw. Unterprozess zu verwenden, wenn das Ausgangssignal einer Verbindung Liste
{}
geleitet wird>
, speichert der Schale die STDOUT- Deskriptor vor der Verbindung Liste ausgeführt wird und stellt sie wieder her , nachdem. Daherexec >
wirkt sich die Liste in der Verbundliste nicht über den Punkt hinaus aus, an dem der alte Deskriptor als STDOUT wiederhergestellt wird.Werfen wir einen Blick auf den relevanten Teil von
strace bash -c '{ exec >/dev/null; } >/dev/null; echo hi' 2>&1 | cat -n
:Sie können sehen, wie in Zeile 134 descriptor
1
(STDOUT
) mindestens auf einen anderen Deskriptor mit Index kopiert wird10
(das ist es, wasF_DUPFD
funktioniert; es gibt den niedrigsten verfügbaren Deskriptor ab der angegebenen Nummer zurück, nachdem er auf diesen Deskriptor dupliziert wurde). Sehen Sie auch, wie in Zeile 137 das Ergebnis vonopen("/dev/null")
(descriptor3
) auf descriptor1
(STDOUT
) kopiert wird . Schließlich wird online147
der alteSTDOUT
gespeicherte Deskriptor10
wieder in descriptor1
(STDOUT
) kopiert . Der Nettoeffekt besteht darin, die ÄnderungSTDOUT
an der Leitung144
(die der inneren entsprichtexec >/dev/null
) zu isolieren .quelle
exec
.Der Unterschied zwischen
{ exec >/dev/null; } >/dev/null; echo "Hi"
und{ exec >/dev/null; }; echo "Hi"
besteht darin, dass die doppelte Umleitung ausgeführt wird,dup2(10, 1);
bevor fd 10 geschlossen wird, bei dem es sich um die Kopie des Originals handeltstdout
, bevor der nächste Befehl ausgeführt wird (echo
).Dies geschieht auf diese Weise, weil die äußere Umleitung die innere Umleitung überlagert. Deshalb kopiert es das Original-
stdout
FD zurück, sobald es fertig ist.quelle