Schließen Sie alle Dateideskriptoren in bash

11

Gibt es eine Möglichkeit, alle geöffneten Dateideskriptoren zu schließen, ohne vorher eine explizite Liste zu haben?

Lorenzo Pistone
quelle
4
Alle welche Dateideskriptoren? Jeder Prozess hat einige offen.
Warren Young
Was sind "alle offenen Dateideskriptoren"? 0, 1 und 2? Oder hast du noch viel mehr? Wenn ja, woher kommen sie?
U. Windl
Werden die Dateideskriptoren nicht automatisch geschlossen, wenn das Skript beendet ist?
Jarno

Antworten:

14

Wörtlich antworten, alle geöffneten Dateideskriptoren schließen für bash:

for fd in $(ls /proc/$$/fd); do
  eval "exec $fd>&-"
done

Dies ist jedoch keine gute Idee, da dadurch die grundlegenden Dateideskriptoren geschlossen werden, die die Shell für die Eingabe und Ausgabe benötigt. Wenn Sie dies tun, wird bei keinem der von Ihnen ausgeführten Programme die Ausgabe auf dem Terminal angezeigt (es sei denn, sie schreiben ttydirekt auf das Gerät). Wenn in meinen Tests durch das Schließen von stdin( exec 0>&-) nur eine interaktive Shell beendet wird.

Möglicherweise möchten Sie tatsächlich alle Dateideskriptoren schließen, die nicht Teil der Grundoperation der Shell sind. Dies sind 0 für stdin, 1 für stdoutund 2 für stderr. Darüber hinaus scheinen in einigen Shells standardmäßig andere Dateideskriptoren geöffnet zu sein. In bashhaben Sie beispielsweise 255 (auch für Terminal-E / A) und in dashI 10, was /dev/ttyeher auf das spezifische tty/ ptsGerät zeigt, das das Terminal verwendet. So schließen Sie alles außer 0, 1, 2 und 255 in bash:

for fd in $(ls /proc/$$/fd); do
  case "$fd" in
    0|1|2|255)
      ;;
    *)
      eval "exec $fd>&-"
      ;;
  esac
done

Beachten Sie auch, dass dies evalerforderlich ist, wenn Sie den in einer Variablen enthaltenen Dateideskriptor umleiten. Wenn dies nicht bashder Fall ist, wird die Variable erweitert, aber als Teil des Befehls betrachtet (in diesem Fall wird versucht, execden Befehl 0oder den 1zu schließenden Dateideskriptor zu verwenden).

HINWEIS: Auch die Verwendung eines Globs anstelle von ls(z. B. /proc/$$/fd/*) scheint einen zusätzlichen Dateideskriptor für den Glob zu öffnen, daher lsscheint dies hier die beste Lösung zu sein.

Aktualisieren

Weitere Informationen zur Portabilität von /proc/$$/fdfinden Sie unter Portabilität von Dateideskriptor-Links . Wenn dies /proc/$$/fdnicht verfügbar ist, wird ein Ersatz für die $(ls /proc/$$/fd)Verwendung von lsof(falls verfügbar) verwendet $(lsof -p $$ -Ff | grep f[0-9] | cut -c 2-).

Graeme
quelle
/procist nur unter Linux verfügbar.
Chepner
4
@chepner Sie hätten Recht, wenn Sie sagten, dass /proc/PID/fddas nicht sehr portabel ist. Zu sagen, dass dies /procnur unter Linux verfügbar ist, ist jedoch keine korrekte Aussage.
Graeme
Einige Websites verwenden das <&-Formular. Ist es anders / erforderlich?
Lorenzo Pistone
@LorenzoPistone, die Richtung spielt beim Schließen eines Deskriptors keine Rolle, sodass beide Formen verwendet werden können.
Graeme
5

In der letzten Versionen von Bash (4.1 und weiter, Jahr 2009 und später) Sie können den Dateideskriptor angeben zu schließen , um einen Shell - Variable mit:

for fd in $(ls /proc/$$/fd/); do
    [ $fd -gt 2 ] && exec {fd}<&-
done

Das Feature war bereits in Korn Shell (seit 1993?), Hat aber anscheinend einige Zeit gebraucht, um in Bash einzudringen.

Tetsujin
quelle
1

Löschen Sie alle Dateideskriptoren mit Ausnahme von i / o / e der aktuellen Shell, schließen Sie jedoch auch die als Argumente angegebenen aus

clear_fds() {
for fd in $(compgen -G "/proc/$BASHPID/fd/*"); do
    fd=${fd/*\/}
        if [[ ! " $* " =~ " ${fd} " ]]; then
            case "$fd" in
                0|1|2|255)
                ;;
                *)
                    eval "exec $fd>&-"
                    ;;
            esac
        fi
done
}
aufreißen
quelle
0

Ein anderer Weg ohne "Auswertung" ist die Verwendung des Formulars:

$ exec {var_a}>> file.txt
$ echo $var_a
10
$ ls -l /proc/self/fd/10
l-wx------ 1 0 0 64 Dec 11 18:32 /proc/self/fd/10 -> /run/user/0/tmp/file.txt
$ echo "aaaaa" >&$var_a
$ cat file.txt
aaaaa
$ exec {var_a}>&-
$ ls /proc/self/fd/10
ls: cannot access '/proc/self/fd/10': No such file or directory
David DG
quelle
Dies schließt jedoch nicht alle Dateideskriptoren.
G-Man sagt "Reinstate Monica"
Tatsächlich. Sie müssen in einer Schleife ausführen, wie in den vorherigen Antworten erläutert ... Es wurde nur darauf hingewiesen, dass "eval" hier nicht obligatorisch ist und dass wir die gleiche Logik beibehalten können, um es zu schließen, als um es zu öffnen. Manpages sind darüber wirklich nicht klar ...
David DG
0

Nein. Der Kernel kann jeweils nur einen FD schließen, und bash verfügt nicht über "Gruppenbefehle" für FDs.

for fd in $(ls -1 /proc/27343/fd); do echo exec $fd">&"-; done

Entfernen Sie das echound das "nach dem Testen.

Wenn dies nicht für die Shell selbst ist, sondern für die Ausführung eines Befehls, können Sie verwenden nohup.

Hauke ​​Laging
quelle
2
Der von verwendete Dateideskriptor >&-kann kein Parameter sein. Die Umleitung wird vor der Parametererweiterung analysiert.
Chepner
1
@chepner exec {fd}>&-funktioniert heutzutage .
Jarno