Si quiero tail
un tail
archivo de texto de 25 GB, ¿el comando lee todo el archivo?
Dado que un archivo puede estar disperso en un disco, imagino que tiene que hacerlo, pero no entiendo bien esas cosas internas.
Si quiero tail
un tail
archivo de texto de 25 GB, ¿el comando lee todo el archivo?
Dado que un archivo puede estar disperso en un disco, imagino que tiene que hacerlo, pero no entiendo bien esas cosas internas.
Respuestas:
No, tail
no lee todo el archivo, busca hasta el final, luego lee los bloques hacia atrás hasta que se haya alcanzado el número esperado de líneas, luego muestra las líneas en la dirección correcta hasta el final del archivo y posiblemente permanece monitoreando el archivo si -f
se usa la opción.
Sin embargo, tail
tenga en cuenta que no tiene más remedio que leer todos los datos si se proporciona una entrada no buscable, por ejemplo, al leer desde una tubería.
Del mismo modo, cuando se le pide que busque líneas que comienzan desde el principio del archivo, con el uso de la tail -n +linenumber
sintaxis o tail +linenumber
la opción no estándar cuando es compatible, tail
obviamente lee todo el archivo (a menos que se interrumpa).
tail +n
leerá todo el archivo, primero para encontrar el número deseado de nuevas líneas, luego para generar el resto.
tail
implementaciones lo hacen o lo hacen correctamente. Por ejemplo, busybox 1.21.1 tail
está roto en ese sentido. También tenga en cuenta que el comportamiento varía cuando tail
ing stdin y donde stdin es un archivo normal y la posición inicial en el archivo no es al principio cuando tail
se invoca (como en { cat > /dev/null; tail; } < file
)
Podrías haber visto cómo tail
funciona tú mismo. Como puede, uno de mis archivos read
se realiza tres veces y en total se leen aproximadamente 10K bytes:
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
muestra qué tail
hacen las llamadas al sistema cuando se ejecuta. Alguna introducción sobre las llamadas al sistema que puede leer aquí en.wikipedia.org/wiki/System_call . Brevemente - abrir - abre un archivo y devuelve un identificador (3 en este ejemplo), lseek
posiciones donde va a leer y read
solo lee y, como puede ver, devuelve cuántos bytes se leen,
Dado que un archivo puede estar disperso en un disco, imagino que tiene que [leer el archivo secuencialmente], pero no entiendo bien esas partes internas.
Como ya sabe, tail
solo busca el final del archivo (con la llamada al sistema lseek
) y funciona al revés. Pero en el comentario citado anteriormente, se pregunta "¿cómo sabe la cola dónde en el disco encontrar el final del archivo?"
La respuesta es simple: Tail no lo sabe. Los procesos a nivel de usuario ven los archivos como flujos continuos, por lo que todo lo que se tail
puede saber es el desplazamiento desde el inicio del archivo. Pero en el sistema de archivos, el "inodo" del archivo (entrada de directorio) está asociado con una lista de números que denotan la ubicación física de los bloques de datos del archivo. Cuando lees el archivo, el núcleo / el controlador del dispositivo descubre qué parte necesitas, calcula su ubicación en el disco y la busca por ti.
Ese es el tipo de cosas para las que tenemos sistemas operativos: para que no tenga que preocuparse por dónde están dispersos los bloques de sus archivos.
Si head
o tail
parece estar leyendo el archivo completo, una razón probable es que el archivo contiene pocos o ningún carácter de nueva línea . Me tropecé con esto hace unos meses con un blob JSON muy grande (gigabytes) que se había serializado sin espacios en blanco, ni siquiera en cadenas.
Si tiene GNU head / tail puede usar -c N
para imprimir el primer / último N bytes en lugar de líneas , pero desafortunadamente esta no es una característica POSIX.
Como puede ver en la línea de código fuente 525, puede ver los comentarios para la implementación.
/* 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. */