Czy ogon czyta cały plik?

113

Jeśli chcę tailplik tekstowy o wielkości 25 GB, czy tailpolecenie odczytuje cały plik?

Ponieważ plik może być rozproszony na dysku, wyobrażam sobie, że musi, ale nie rozumiem dobrze takich elementów wewnętrznych.

The Unfun Cat
źródło

Odpowiedzi:

119

Nie, tailnie odczytuje całego pliku, szuka do końca, a następnie odczytuje bloki do tyłu, aż do osiągnięcia oczekiwanej liczby linii, a następnie wyświetla linie we właściwym kierunku do końca pliku i prawdopodobnie nadal monitoruje plik, jeśli -fopcja jest używana.

Zauważ jednak, że tailnie ma wyboru, jak tylko odczytać wszystkie dane, jeśli nie są widoczne, na przykład podczas odczytu z potoku.

Podobnie, gdy zostanie poproszony o wyszukanie wierszy zaczynających się od początku pliku, przy użyciu tail -n +linenumberskładni lub tail +linenumberniestandardowej opcji, gdy jest obsługiwana, tailoczywiście czyta cały plik (chyba że jest przerwany).

jlliagre
źródło
14
Cholera, za szybko :-). Oto odpowiedni kod źródłowy . Wydrukuj ostatnie N_LINES wiersze od końca pliku FD. Cofnij się do tyłu pliku, odczytując bajty „BUFSIZ” na raz (z wyjątkiem prawdopodobnie pierwszego), dopóki nie dojdziemy do początku pliku lub nie przeczytamy NUMERU nowej linii.
Patrick,
1
Ponadto, tail +nbędzie przeczytać cały plik - najpierw znaleźć odpowiednią liczbę nowych linii, a następnie do wyjścia resztę.
SF.
@SF. Rzeczywiście, odpowiedź zaktualizowana.
jlliagre
4
Pamiętaj, że nie wszystkie tailimplementacje robią to lub robią to poprawnie. Na przykład zajęty 1.21.1 tailjest pod tym względem zepsuty. Zauważ też, że zachowanie zmienia się, gdy tailing jest stdin i gdzie stdin jest zwykłym plikiem, a początkowa pozycja w pliku nie jest na początku, gdy tailjest wywoływana (jak w { cat > /dev/null; tail; } < file)
Stéphane Chazelas
4
@StephaneChazelas * nix - świat dziwnych skrzynek staje się normalny. (Choć dane wejściowe widoczne i niewidzialne są zdecydowanie ważnym punktem).
CVn
69

Mogłeś zobaczyć, jak to taildziała. Jak można, jeden z moich plików readjest wykonywany trzykrotnie i w sumie odczytywanych jest około 10 000 bajtów:

strace 2>&1  tail ./huge-file >/dev/null  | grep -e "read" -e "lseek" -e "open" -e "close"
open("./huge-file", O_RDONLY)           = 3
lseek(3, 0, SEEK_CUR)                   = 0
lseek(3, 0, SEEK_END)                   = 80552644
lseek(3, 80551936, SEEK_SET)            = 80551936
read(3, ""..., 708) = 708
lseek(3, 80543744, SEEK_SET)            = 80543744
read(3, ""..., 8192) = 8192
read(3, ""..., 708) = 708
close(3)                                = 0
Siergiej Kurenkow
źródło
Nie rozumiem, jak to odpowiada na pytanie. Czy możesz wyjaśnić, co się tutaj dzieje?
Iain Samuel McLean Starszy
10
stracepokazuje, co tailrobią wywołania systemowe po uruchomieniu. Kilka wstępów na temat wywołań systemowych można przeczytać tutaj en.wikipedia.org/wiki/System_call . W skrócie - otwórz - otwiera plik i zwraca uchwyt (3 w tym przykładzie), lseekpozycje, w których zamierzasz czytać i readpo prostu czyta, a jak widzisz, zwraca liczbę odczytanych bajtów,
Siergiej Kurenkow
2
Analizując wywołania systemowe, możesz czasem zrozumieć, jak działa program.
Siergiej Kurenkow
26

Ponieważ plik może być rozproszony na dysku, wyobrażam sobie, że musi on [odczytać plik sekwencyjnie], ale nie rozumiem takich wewnętrznych elementów.

Jak już wiesz, tailpo prostu szuka końca pliku (z wywołaniem systemowym lseek) i działa wstecz. Ale w przytoczonej wyżej uwadze zastanawiasz się, „skąd ogon wie, gdzie na dysku znaleźć koniec pliku?”

Odpowiedź jest prosta: ogon nie wie. Procesy na poziomie użytkownika widzą pliki jako ciągłe strumienie, więc wszyscy tailmogą wiedzieć, że jest przesunięciem od początku pliku. Ale w systemie plików „i-węzeł” pliku (pozycja katalogu) jest powiązany z listą liczb oznaczających fizyczną lokalizację bloków danych pliku. Kiedy czytasz z pliku, jądro / sterownik urządzenia ustalają, której części potrzebujesz, ustala swoją lokalizację na dysku i pobiera ją za ciebie.

To jest coś, do czego mamy systemy operacyjne: więc nie musisz się martwić, gdzie bloki twojego pliku są rozproszone.

Alexis
źródło
2

Jeśli headlub tail wydaje się czytać cały plik, prawdopodobnym powodem jest to, że plik zawiera niewiele znaków nowej linii lub nie zawiera ich wcale . Potknąłem się o to kilka miesięcy temu z bardzo dużą (gigabajtami) kroplą JSON, która została serializowana bez żadnych spacji, nawet w ciągach.

Jeśli masz głowę / ogon GNU, możesz użyć -c Ndo wydrukowania pierwszego / ostatniego N bajtów zamiast linii , ale niestety nie jest to funkcja POSIX.

zwol
źródło
1

Jak widać w wierszu kodu źródłowego 525, można zobaczyć komentarze do implementacji.

 /* Print the last N_LINES lines from the end of file FD.
   Go backward through the file, reading 'BUFSIZ' bytes at a time (except
   probably the first), until we hit the start of the file or have
   read NUMBER newlines.
   START_POS is the starting position of the read pointer for the file
   associated with FD (may be nonzero).
   END_POS is the file offset of EOF (one larger than offset of last byte).
   Return true if successful.  */
Seenivasan
źródło