Jeśli chcę tail
plik tekstowy o wielkości 25 GB, czy tail
polecenie 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.
Jeśli chcę tail
plik tekstowy o wielkości 25 GB, czy tail
polecenie 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.
Odpowiedzi:
Nie, tail
nie 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 -f
opcja jest używana.
Zauważ jednak, że tail
nie 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 +linenumber
składni lub tail +linenumber
niestandardowej opcji, gdy jest obsługiwana, tail
oczywiście czyta cały plik (chyba że jest przerwany).
tail +n
będzie przeczytać cały plik - najpierw znaleźć odpowiednią liczbę nowych linii, a następnie do wyjścia resztę.
tail
implementacje robią to lub robią to poprawnie. Na przykład zajęty 1.21.1 tail
jest pod tym względem zepsuty. Zauważ też, że zachowanie zmienia się, gdy tail
ing jest stdin i gdzie stdin jest zwykłym plikiem, a początkowa pozycja w pliku nie jest na początku, gdy tail
jest wywoływana (jak w { cat > /dev/null; tail; } < file
)
Mogłeś zobaczyć, jak to tail
działa. Jak można, jeden z moich plików read
jest 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
strace
pokazuje, co tail
robią 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), lseek
pozycje, w których zamierzasz czytać i read
po prostu czyta, a jak widzisz, zwraca liczbę odczytanych bajtów,
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, tail
po 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 tail
mogą 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.
Jeśli head
lub 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 N
do wydrukowania pierwszego / ostatniego N bajtów zamiast linii , ale niestety nie jest to funkcja POSIX.
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. */