Se eu quiser tail
um arquivo de texto de 25 GB, o tail
comando lê o arquivo inteiro?
Como um arquivo pode estar espalhado em um disco, imagino que seja necessário, mas não entendo bem esses internos.
Se eu quiser tail
um arquivo de texto de 25 GB, o tail
comando lê o arquivo inteiro?
Como um arquivo pode estar espalhado em um disco, imagino que seja necessário, mas não entendo bem esses internos.
Respostas:
Não, tail
não lê o arquivo inteiro, procura até o fim e depois lê blocos para trás até que o número esperado de linhas seja atingido, depois exibe as linhas na direção correta até o final do arquivo e, possivelmente, continua monitorando o arquivo se a -f
opção for usada.
Observe, no entanto, que tail
não há escolha a não ser ler todos os dados, se for fornecida uma entrada não procurável, por exemplo, ao ler de um tubo.
Da mesma forma, quando solicitado a procurar linhas a partir do início do arquivo, com o uso da tail -n +linenumber
sintaxe ou da tail +linenumber
opção não padrão, quando suportado, tail
obviamente lê o arquivo inteiro (a menos que seja interrompido).
tail +n
lerá o arquivo inteiro - primeiro para encontrar o número desejado de novas linhas e depois para o restante.
tail
implementações fazem ou fazem isso corretamente. Por exemplo, o busybox 1.21.1 tail
está quebrado nesse sentido. Observe também que o comportamento varia quando tail
ing stdin e onde stdin é um arquivo regular e a posição inicial no arquivo não está no início, quando tail
é invocado (como em { cat > /dev/null; tail; } < file
)
Você poderia ter visto como tail
funciona a si mesmo. Como você pode, um dos meus arquivos read
é feito três vezes e, no total, são lidos 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
mostra o que as chamadas tail
do sistema fazem quando são executadas. Algumas apresentações sobre chamadas do sistema, você pode ler aqui en.wikipedia.org/wiki/System_call . Resumidamente - aberta - abre um arquivo e retorna uma alça (3 neste exemplo), lseek
posições onde você está indo para ler e read
apenas lê e como você pode ver ele retorna quantos bytes são lidos,
Como um arquivo pode estar espalhado em um disco, imagino que ele tenha que [ler o arquivo sequencialmente], mas não entendo bem esses internos.
Como você sabe agora, tail
apenas procura o final do arquivo (com a chamada do sistema lseek
) e trabalha para trás. Mas na observação citada acima, você está se perguntando "como a cauda sabe onde está o disco para encontrar o final do arquivo?"
A resposta é simples: cauda não sabe. Os processos no nível do usuário veem os arquivos como fluxos contínuos, para que todos tail
saibam o deslocamento desde o início do arquivo. Mas no sistema de arquivos, o "inode" (entrada de diretório) do arquivo está associado a uma lista de números que indicam a localização física dos blocos de dados do arquivo. Quando você lê o arquivo, o kernel / driver de dispositivo descobre qual parte você precisa, calcula sua localização no disco e a busca por você.
Esse é o tipo de coisa para a qual temos sistemas operacionais: assim você não precisa se preocupar com a dispersão dos blocos de arquivos.
Se head
ou tail
parece estar lendo o arquivo inteiro, um motivo provável é que o arquivo contenha poucos ou nenhum caractere de nova linha . Tropecei nisso há alguns meses com um blob JSON muito grande (gigabytes) que havia sido serializado sem nenhum espaço em branco, nem mesmo em cadeias.
Se você possui GNU head / tail, pode usar -c N
para imprimir o primeiro / último N bytes em vez de linhas , mas infelizmente este não é um recurso POSIX.
Como você pode ver na linha de código-fonte 525, você pode ver os comentários para a implementação.
/* 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. */