OS X, bash: mniej działa na otwartych deskryptorach plików, cat nie

10

W skrypcie bash, nad którym pracuję (który musi działać na Ubuntu i OS X), muszę przekierować wyjście setek poleceń do pliku.
Zamiast dołączać &>...do nich wszystkich, po prostu robię

exec 9>&1
exec 5<>/tmp/some-file.txt
exec 1>&5

Jak dotąd tak dobrze, ale w połowie tych wszystkich poleceń muszę przeczytać wszystko, co zostało napisane do tej pory, jednocześnie pozostawiając otwarty deskryptor pliku.
Teraz na Ubuntu mogę po prostu to zrobić

cat /dev/fd/5

lub

tee </dev/fd/5

ale w OS X nic nie jest drukowane (a polecenia wychodzą natychmiast).
Jednak używając lessWidzę zawartość pliku na obu.
Mogę osiągnąć powyższy efekt (działanie na obu systemach operacyjnych) za pomocą

less /dev/fd/5 | tee

ale to wygląda na włamanie.

Dlaczego więc lessnajwyraźniej widzi rzeczy, catktórych nie można uzyskać w systemie OS X? (A może dotyczy to wszystkich potomków BSD?)
Czy robię coś złego?

Siguza
źródło

Odpowiedzi:

13

W OS X, podobnie jak we wszystkich systemach, w których są obsługiwane oprócz Linuksa , otwieranie /dev/fd/xjest jak robienie dup(x), wynikowy fd mniej więcej wskazuje ten sam opis otwartego pliku jak na fd x, a w szczególności będzie miał takie samo przesunięcie w pliku.

Linux jest tutaj wyjątkiem. W systemie Linux /dev/fd/xjest dowiązaniem do /proc/self/fd/xi /proc/self/fd/xjest pseudo-dowiązaniem do otwartego pliku na fd x. W systemie Linux, gdy to zrobisz open("/dev/fd/x", somemode), otrzymasz zupełnie nowy opis otwartego pliku do tego samego pliku, co otwarty x. Otrzymany nowy fd nie jest w żaden sposób powiązany z fd x. W szczególności przesunięcie będzie na początku pliku (chyba że otworzysz go O_APPENDoczywiście), a tryb (odczyt / zapis / dołącz ...) może różnić się od tego na fd x (możesz nawet uzyskać coś zupełnie innego niż na fd x, jak drugi koniec potoku podczas otwierania go w trybie przeciwnym). (Oznacza to również, że nie działa to na przykład dla gniazd, których nie można otworzyć () ).

Więc w Linuksie, kiedy to robisz

exec 5<> file
echo test >&5

Przesunięcie fd 5 znajduje się na końcu pliku. Jeśli zrobisz

cat <&5

Nic nie dostaniesz

Nadal kiedy to robisz:

cat /dev/fd/5

Widzisz, testponieważ catdostaje nowy fd tylko do odczytu, fileniezwiązany z fd 5.

W innych systemach

cat /dev/fd/5

cat dostaje fd, który jest duplikatem fd 5, więc nadal z przesunięciem na końcu pliku.

Powodem, dla którego działa, lessjest to, że z jakiegoś powodu lessrobi to lseek()na początku pliku ( lseek(1); lseek(0)sprawdza, czy plik jest widoczny).

Tutaj prawdopodobnie chcesz mieć fd do czytania i jeden do pisania, jeśli chcesz, aby oba miały różne przesunięcia:

exec 5< file 9>&1 > file

Lub będziesz musiał ponownie otworzyć plik, jeśli nadal istnieje, lub zrobić lseek()jak lessrobi.

ksh93i zshsą jedynymi powłokami z wbudowanym lseek()operatorem:

cat <&5 <#((0)) # ksh93
{sysseek 0; cat} <&5 # zsh, zmodload zsh/system to enable that builtin

Lub:

cat /dev/fd/5 5<#((0))  # ksh93
sysseek -u 5 0; cat /dev/fd/5 # zsh
Stéphane Chazelas
źródło