Deskryptory plików i skrypty powłoki

32

Bardzo trudno mi zrozumieć, w jaki sposób można używać deskryptorów plików w skryptach powłoki.

Znam podstawy takie jak

exec 5 > /tmp/foo

Tak więc fd 5 jest dołączony do foo do pisania.

exec 6 < /tmp/bar

… do czytania.

exec 5>&-

… Zamknij fd.

Co to teraz robi?

#!/bin/bash

exec 5 > /tmp/foo 
exec 6 < /tmp/bar 

cat <&6 | while read a
do
     echo $a >&5
done

Jak rozumiem, &5zamyka fd, więc w jaki sposób wyjście jest nadal przekierowywane po każdym wywołaniu?

To jest kopia makaronu z: tutaj

Twierdzi, że użycie tego w prosty echo $a > filesposób sprawiłoby, że byłoby to znacznie szybsze, ale nie rozumiem. Byłbym wdzięczny za wszelkie linki do porządnego samouczka. Wydaje mi się, że moce go zawiodły.

Ricko M.
źródło

Odpowiedzi:

44

Po pierwsze, zwróć uwagę, że składnia zamykania jest 5>&-lub 6<&-, w zależności od tego, czy deskryptor pliku jest odczytywany do zapisu, czy do odczytu. Wydaje się, że w tym wpisie na blogu występuje literówka lub usterka formatowania.

Oto skomentowany skrypt.

exec 5>/tmp/foo       # open /tmp/foo for writing, on fd 5
exec 6</tmp/bar       # open /tmp/bar for reading, on fd 6
cat <&6 |             # call cat, with its standard input connected to
                      # what is currently fd 6, i.e., /tmp/bar
while read a; do      # 
  echo $a >&5         # write to fd 5, i.e., /tmp/foo
done                  # 

Tu nie ma zamknięcia. Ponieważ w tym prostym przykładzie wszystkie dane wejściowe i wyjściowe idą w to samo miejsce, użycie dodatkowych deskryptorów plików nie jest konieczne. Mógłbyś pisać

cat </tmp/bar |
while read a; do
  echo $a
done >/tmp/foo

Używanie jawnych deskryptorów plików staje się przydatne, gdy chcesz pisać po kolei do wielu plików. Rozważmy na przykład skrypt, który wysyła dane do pliku wyjściowego danych i rejestruje dane do pliku dziennika, a także ewentualne komunikaty o błędach. Oznacza to trzy kanały wyjściowe: jeden dla danych, jeden dla dzienników i jeden dla błędów. Ponieważ istnieją tylko dwa standardowe deskryptory dla danych wyjściowych, potrzebny jest trzeci. Możesz zadzwonić, execaby otworzyć pliki wyjściowe:

exec >data-file
exec 3>log-file
echo "first line of data"
echo "this is a log line" >&3

if something_bad_happens; then echo error message >&2; fi
exec >&-  # close the data output file
echo "output file closed" >&3

Uwaga o wydajności pojawia się, gdy masz przekierowanie w pętli, takie jak to (zakładając, że plik jest pusty na początek):

while …; do echo $a >>/tmp/bar; done

Przy każdej iteracji program otwiera się /tmp/bar, szuka końca pliku, dołącza pewne dane i zamyka plik. Bardziej efektywne jest otwarcie pliku raz na zawsze:

while …; do echo $a; done >/tmp/bar

Gdy występuje wiele przekierowań w różnych momentach, execprzydatne staje się wywołanie przekierowania zamiast zawijania bloku w przekierowaniu.

exec >/tmp/bar
while …; do echo $a; done

Znajdziesz kilka innych przykładów przekierowania, przeglądając io-redirectiontag na tej stronie .

Gilles „SO- przestań być zły”
źródło
1
Każda składnia deskryptora zamykającego plik faktycznie robi to samo.
user1338062,