Zamknij wszystkie deskryptory plików w bash

13

Czy istnieje sposób na zamknięcie wszystkich otwartych deskryptorów plików bez uprzedniej ich jawnej listy?

Lorenzo Pistone
źródło
4
Wszystkie który plik deskryptory? Każdy proces ma pewne otwarte.
Warren Young,
Co to są „wszystkie otwarte deskryptory plików”? 0, 1 i 2? A może masz o wiele więcej? Jeśli tak, to skąd one pochodzą?
U. Windl
Czy deskryptory plików nie są zamykane automatycznie po zakończeniu skryptu?
jarno

Odpowiedzi:

15

Aby odpowiedzieć dosłownie, zamknąć wszystkie otwarte deskryptory plików dla bash:

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

Jednak to naprawdę nie jest dobry pomysł, ponieważ spowoduje zamknięcie podstawowych deskryptorów plików wymaganych przez powłokę dla danych wejściowych i wyjściowych. Jeśli to zrobisz, żaden z uruchomionych programów nie będzie wyświetlał danych wyjściowych na terminalu (chyba że zapisują ttybezpośrednio w urządzeniu). Jeśli fakt w moich testach zamknięcie stdin( exec 0>&-) powoduje jedynie zamknięcie interaktywnej powłoki.

To, czego właściwie możesz chcieć, to raczej zamknąć wszystkie deskryptory plików, które nie są częścią podstawowej operacji powłoki. Są to 0 dla stdin, 1 dla stdouti 2 dla stderr. Ponadto niektóre powłoki wydają się mieć domyślnie otwarte inne deskryptory plików. W bash, na przykład, mieć 255 (również dla zacisku wejścia / wyjścia) oraz dash, że nie więcej niż 10, co wskazuje /dev/tty, a nie konkretnych tty/ ptsurządzenia terminala jest używany. Aby zamknąć wszystko oprócz 0, 1, 2 i 255 w bash:

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

Należy również zauważyć, że evaljest wymagana, gdy przekierowanie deskryptor pliku zawartego w zmiennej, jeśli nie bashwzrośnie zmienną, ale uważają, że część komendy (w tym przypadku byłoby spróbować execpolecenia 0lub 1lub inny plik deskryptora staramy się blisko).

UWAGA: Również użycie glob zamiast ls(np. /proc/$$/fd/*) Wydaje się otwierać dodatkowy deskryptor pliku dla glob, więc lswydaje się tutaj najlepszym rozwiązaniem.

Aktualizacja

Aby uzyskać więcej informacji na temat przenośności /proc/$$/fd, zobacz Przenośność linków deskryptorów plików . Jeśli /proc/$$/fdjest niedostępne, oznacza to spadek w zamian za $(ls /proc/$$/fd)użycie lsof(jeśli jest dostępne) $(lsof -p $$ -Ff | grep f[0-9] | cut -c 2-).

Graeme
źródło
/procjest dostępny tylko w systemie Linux.
chepner
4
@chepner miałbyś rację, gdybyś powiedział, że /proc/PID/fdto nie jest bardzo przenośne. Ale powiedzenie, że /procjest dostępne tylko pod Linuksem, nie jest poprawnym stwierdzeniem.
Graeme,
Niektóre strony korzystają z <&-formularza, czy jest on inny / potrzebny?
Lorenzo Pistone
@LorenzoPistone, kierunek nie ma znaczenia przy zamykaniu deskryptora, więc można użyć dowolnej formy.
Graeme,
5

W ostatnich wersjach bash (4.1 i nowsze, rok 2009 i później), które można określić deskryptor pliku do zamknięcia za pomocą zmiennej powłoki:

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

Ta funkcja była już w skorupie Korna (od 1993 roku?), Ale najwyraźniej zajęła trochę czasu, aby dostać się do Bash.

tetsujin
źródło
1

Wyczyść wszystkie deskryptory plików oprócz I / O / e bieżącej powłoki, ale również wyklucza te podane jako argumenty

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
}
odtąd
źródło
0

Innym sposobem bez „ewaluacji” jest użycie formularza:

$ 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
źródło
Ale to nie zamyka wszystkich deskryptorów plików.
G-Man mówi „Reinstate Monica”
W rzeczy samej. Będziesz musiał wykonać pętlę, jak wyjaśniono w poprzednich odpowiedziach ... Chodziło tylko o wskazanie, że „eval” nie jest tutaj obowiązkowe i że możemy zachować tę samą logikę, aby ją zamknąć niż otworzyć ... Strony podręcznika naprawdę nie są jasne na ten temat ...
David DG
0

Nie. Jądro może zamykać tylko jedną FD na raz, a bash nie ma „komend grupowych” dla FD.

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

Usuń echoi "po testach.

Jeśli nie dotyczy to samej powłoki, ale polecenia, które należy uruchomić, możesz użyć nohup.

Hauke ​​Laging
źródło
2
Używany deskryptor pliku >&-nie może być parametrem; przekierowanie jest analizowane przed rozszerzeniem parametru.
chepner
1
@chepner obecnie exec {fd}>&-działa.
jarno