Ich habe ein bestimmtes Bash-Skript, das den ursprünglichen /dev/stdout
Speicherort beibehalten soll, bevor der erste Dateideskriptor durch einen anderen Speicherort ersetzt wird.
Also schrieb ich natürlich so etwas
old_stdout=$(readlink -f /dev/stdout)
Und es hat nicht funktioniert. Sehr schnell verstehe ich, was das Problem war:
test@ubuntu:~$ echo $(readlink -f /dev/stdout)
/proc/5175/fd/pipe:[31764]
test@ubuntu:~$ readlink -f /dev/stdout
/dev/pts/18
Es ist $()
klar, dass die Ausführung in einer Unterschale erfolgt, die an die übergeordnete Shell weitergeleitet wird.
Die Frage ist also: Gibt es eine zuverlässige (auf Portabilität zwischen Linux-Distributionen beschränkte) Möglichkeit, den Speicherort /dev/stdout
als Zeichenfolge in einem Bash-Skript zu speichern ?
bash
io-redirection
io
alexey.e.egorov
quelle
quelle
/dev/stdout
das Problem beim Drucken von Nachrichten im unbeaufsichtigten Modus lösen würde. Alternativ kann jede andere Aktion, die eine Ausgabe erzeugt, umgeleitet werden, und es gibt eine ganze Reihe von Aktionen. Etwa 100-mal mehr als Benutzerinteraktionsnachrichten.stderr
. Dies ist zum Beispiel der Grund, warum Eingabeaufforderungenstderr
standardmäßig angezeigt werden.stderr
muss das auch umgeleitet und gespeichert werden, da das Skript eine Reihe von externen Programmen aufruft und alle möglichen Fehlermeldungen gesammelt und protokolliert werden sollen.Antworten:
Um einen Dateideskriptor zu speichern, duplizieren Sie ihn auf einem anderen fd. Das Speichern eines Pfads zur entsprechenden Datei reicht nicht aus. Sie müssen den Öffnungsmodus, die Öffnungsflags, die aktuelle Position in der Datei usw. speichern. Und natürlich würde das bei anonymen Pipes oder Sockets nicht funktionieren, da diese keinen Pfad haben. Was Sie speichern möchten, ist die offene Dateibeschreibung , auf die sich die FD bezieht, und das Duplizieren einer FD gibt tatsächlich eine neue FD an dieselbe offene Dateibeschreibung zurück .
Um einen Dateideskriptor mit einer Bourne-ähnlichen Shell auf einen anderen zu duplizieren, lautet die Syntax wie folgt:
Oben ist fd 1 auf fd 3 dupliziert.
Was auch immer fd 3 zuvor bereits geöffnet war, wird geschlossen. Beachten Sie jedoch, dass fds 3 bis 9 (normalerweise mehr, bis zu 99 mit
yash
) für diesen Zweck reserviert sind (und entgegen 0, 1 oder 2 keine spezielle Bedeutung haben) Shell weiß, dass sie nicht für interne Zwecke eingesetzt werden dürfen. Der einzige Grund, warum fd 3 zuvor geöffnet gewesen wäre, ist, dass Sie es in Skript 1 getan haben oder dass es vom Aufrufer durchgesickert ist.Dann können Sie stdout in etwas anderes ändern:
Und später, um stdout wiederherzustellen:
(
3>&-
um den Dateideskriptor zu schließen, den wir nicht mehr benötigen).Das Problem dabei ist, dass außer in ksh jeder Befehl, den Sie danach ausführen,
exec 3>&1
das fd 3 erbt. Das ist ein fd-Leck. Im Allgemeinen keine große Sache, aber das kann zu Problemen führen.ksh
setzt das Close-On-Exec- Flag auf diesen FDS (für FDS über 2), aber andere Shells und andere Shells haben keine Möglichkeit, dieses Flag manuell zu setzen.Die Arbeit für die andere Shell besteht darin, fd 3 für jeden einzelnen Befehl zu schließen, wie zum Beispiel:
Schwerfällig. Hier wäre der beste Weg, überhaupt nicht zu verwenden
exec
, sondern Befehlsgruppen umzuleiten:Dort ist es die Shell, die dafür sorgt, dass stdout gespeichert und anschließend wiederhergestellt wird (und dies geschieht intern, indem es auf einem fd (über 9, über 99 für
yash
) mit gesetztem Flag close-on-exec dupliziert wird ).Anmerkung 1
Jetzt kann die Verwaltung dieser FDS 3 bis 9 mühsam und problematisch sein, wenn Sie sie ausgiebig oder in Funktionen verwenden, insbesondere wenn Ihr Skript Code von Drittanbietern verwendet, der diese FDS wiederum verwenden kann.
Einige Schalen (
zsh
,bash
,ksh93
, alle Features (hinzugefügt vorgeschlagen von Oliver Kiddle vonzsh
) etwa zur gleichen Zeit im Jahr 2005 , nachdem sie unter ihren Entwicklern diskutiert wurden) eine alternative Syntax , um die erste freie fd über 10 statt zuzuordnen , die in diesem Fall hilft:quelle
rc.local
Dienst ausgeführt wird, zexec {FD}>&1
. Dies wird jedoch nur in Bash 4 unterstützt, was wirklich traurig ist. Das ist also nicht wirklich portabel.eval "exec $i>&1"
ist eine Sache, die ich vermeiden möchte, weil es umständlich ist. Kann ich mich wirklich darauf verlassen, dass fds über 9 dann kostenlos wären?bash
Lass dich in den Fuß schießen.Wie Sie sehen, ist Bash-Scripting keine normale Programmiersprache, in der Sie Dateideskriptoren zuweisen können.
Die einfachste Lösung besteht darin, eine Sub-Shell zu verwenden, um das auszuführen, was umgeleitet werden soll, damit die Verarbeitung auf die Top-Shell zurückgesetzt werden kann, deren Standard-E / A intakt ist.
Eine alternative Lösung wäre
tty
, das TTY-Gerät zu identifizieren und die E / A in Ihrem Skript zu steuern. Beispielsweise:und dann kannst du ..
quelle
$$
würde Ihnen die aktuelle Prozess-PID geben, im Falle einer interaktiven Shell oder eines Skripts die entsprechende Shell-PID.So können Sie verwenden:
Beispiel:
quelle
/proc
Struktur Portabilitätsprobleme, ebenso wie die Verwendung,/dev/stdout
wie in der Frage erwähnt./proc
Struktur? Es würde auf jedem Linux funktionieren, das hatprocfs
..procfs
es fast immer der Fall ist, aber wir sehen oft Fragen zur Portabilität und eine gute Entwicklungsmethode beinhaltet die Berücksichtigung der Portabilität auf andere Systeme.bash
kann auf einer Vielzahl von Betriebssystemen ausgeführt werden.