Tail lit-il l'intégralité du fichier?

113

Si je veux tailun fichier texte de 25 Go, la tailcommande lit-elle l'intégralité du fichier?

Étant donné qu’un fichier peut être dispersé sur un disque, j’imagine qu’il doit le faire, mais je ne comprends pas bien ces éléments internes.

Le chat
la source

Réponses:

119

Non, tailne lit pas tout le fichier, il cherche la fin puis lit les blocs à l'envers jusqu'à atteindre le nombre de lignes attendu, puis affiche les lignes dans le bon sens jusqu'à la fin du fichier et reste éventuellement en veille. fichier si l' -foption est utilisée.

Notez cependant que tailn’a pas d’autre choix que de lire l’ensemble des données si une entrée non recherchée est fournie, par exemple lors de la lecture d’un canal.

De même, lorsqu'on lui demande de rechercher des lignes à partir du début du fichier, avec l'aide de la tail -n +linenumbersyntaxe ou de tail +linenumberl'option non standard lorsque pris en charge, taillit intégralement le fichier (sauf si interrompu).

jlliagre
la source
14
Zut, trop vite :-). Voici le code source pertinent . Imprimez les dernières lignes N_LINES à partir de la fin du fichier FD. Revenez en arrière dans le fichier, en lisant octets 'BUFSIZ' à la fois (sauf probablement le premier), jusqu'à ce que nous arrivions au début du fichier ou que nous ayons lu NUMBER Newlines.
Patrick
1
En outre, tail +nlira l’ensemble du fichier - d’abord pour trouver le nombre souhaité de nouvelles lignes, puis pour afficher le reste.
SF.
@SF. En effet, réponse mise à jour.
Jlliagre
4
Notez que toutes les tailimplémentations ne le font pas ou ne le font pas correctement. Par exemple, busybox 1.21.1 tailest cassé à cet égard. Notez également que le comportement varie lorsque tailstdin est utilisé et que stdin est un fichier normal et que la position initiale dans le fichier n’est pas au début lorsqu’elle tailest invoquée (comme dans { cat > /dev/null; tail; } < file)
Stéphane Chazelas
4
@StephaneChazelas * nix - le monde des cas étranges devenant normal. (Bien que les entrées recherchées ou non recherchées soient définitivement un argument valable.)
CVn
69

Vous auriez pu voir comment ça tailmarche vous-même. Comme vous pouvez le faire pour un de mes fichiers readest fait trois fois et au total environ 10K octets sont lus:

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
Sergei Kurenkov
la source
Je ne vois pas comment cela répond à la question. Pouvez-vous expliquer ce qui se passe ici?
Iain Samuel McLean Elder
10
stracemontre ce que tailfont les appels système lors de l'exécution. Vous pouvez lire un peu d’introduction sur les appels système en.wikipedia.org/wiki/System_call . En bref - ouvrir - ouvre un fichier et retourne un descripteur (3 dans cet exemple), les lseekpositions où vous allez lire et readsimplement lire et comme vous pouvez le voir, combien d'octets lus sont lus,
Sergei Kurenkov
2
Ainsi, en analysant les appels système, vous pouvez parfois comprendre le fonctionnement d’un programme.
Sergei Kurenkov
26

Puisqu'un fichier peut être dispersé sur un disque, j'imagine qu'il doit [lire le fichier séquentiellement], mais je ne comprends pas bien ces éléments internes.

Comme vous le savez maintenant, tailcherche simplement la fin du fichier (avec l'appel système lseek), et fonctionne à l'envers. Mais dans la remarque citée ci-dessus, vous vous demandez "comment tail ne sait où sur le disque trouver la fin du fichier?"

La réponse est simple: la queue ne sait pas. Les processus au niveau utilisateur voient les fichiers comme des flux continus, de sorte que tout tailpeut être connu, c'est le décalage par rapport au début du fichier. Mais dans le système de fichiers, "l'inode" du fichier (entrée de répertoire) est associé à une liste de nombres indiquant l'emplacement physique des blocs de données du fichier. Lorsque vous lisez le fichier, le noyau / le pilote de périphérique détermine la partie dont vous avez besoin, détermine son emplacement sur le disque et le recherche à votre place.

C'est le genre de chose pour laquelle nous avons un système d'exploitation: vous n'avez donc pas à vous soucier de la répartition des blocs de votre fichier.

alexis
la source
2

Si headou tail semble lire tout le fichier, cela tient probablement au fait que le fichier contient peu ou pas de caractères de nouvelle ligne . J'y ai trébuché il y a quelques mois avec un très gros blob JSON (sérialisé) sérialisé sans aucun espace, même pas dans des chaînes.

Si vous avez une tête / queue GNU, vous pouvez utiliser -c Npour imprimer le premier / dernier N octets au lieu de lignes , mais malheureusement, ce n’est pas une fonctionnalité POSIX.

zwol
la source
1

Comme vous pouvez le voir dans la ligne de code source 525, vous pouvez voir les commentaires pour l'implémentation.

 /* 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
la source