Observo que, no Ubuntu 24.04.2 com coreutils
versão 9.4-3ubuntu6
, executando:
$ tail -c 4097 /dev/zero
$ echo $?
0
sai imediatamente com um código de status 0. Eu esperava que o comando bloqueasse indefinidamente, já que /dev/zero é um fluxo infinito.
Em contraste, os seguintes comandos se comportam conforme o esperado (ou seja, eles bloqueiam até serem interrompidos):
$ tail -c 4096 /dev/zero
^C
$ echo $?
130
$ cat /dev/zero | tail -c 4097
^C
$ echo $?
130
Tentativa de depuração
A saída do strace mostra diferenças entre as duas invocações:
strace tail -c 4096 /dev/zero | strace tail -c 4097 /dev/zero |
---|---|
… | … |
fechar(3) = 0 | fechar(3) = 0 |
openat(AT_FDCWD, "/dev/zero", O_RDONLY) = 3 | openat(AT_FDCWD, "/dev/zero", O_RDONLY) = 3 |
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(0x1, 0x5), …}) = 0 | fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(0x1, 0x5), …}) = 0 |
lseek(3, -4096, FIM_DE_BUSCA) = 0 | lseek(3, -4097, FIM_DE_BUSCA) = 0 |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | leia(3, "\0\0\0\0\0\0\0\0\0\…, 4097) = 4097 |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | fstat(1, {st_mode=S_IFIFO|0600, st_size=0, …}) = 0 |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | escreva(1, "\0\0\0\0\0\0\0\0\0\…, 4096 |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | fechar(3) = 0 |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | escreva(1, "\0", 1) = 1 |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | fechar(1) = 0 |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | fechar(2) = 0 |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | exit_group(0) = ? |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | ~~+~~ saiu com 0 ~~+~~ |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | |
leia(3, "\0\0\0\0\0\0\0\0\0"…, 8192) = 8192 | |
… |
A primeira coisa a ser observada é que os arquivos de dispositivo não podem oferecer suporte a
seek
; talvez surpreendentemente, a busca em tais arquivos seja bem-sucedida sem mover a posição no arquivo.Antes da versão 9.6, o coreutils
tail
tentava buscar a partir do final e calculava uma posição final com base na posição após a busca (independentemente de quantos bytes fossem solicitados após a posição). Assim, com/dev/zero
, ele decidia que precisava ler da posição 0 até n . Em seguida, ele analisava a quantidade de dados a serem lidos para determinar como lê-los: se fosse uma página (no seu caso, 4.096 bytes) ou menos, ele canalizaria até o final do arquivo; se fosse mais de uma página, ele despejaria o número necessário de bytes. Crucialmente, o canalização aqui continua lendo até o final do arquivo, o que nunca acontece! Portanto, ler mais do que o valor de uma página de dados é concluído, enquanto ler uma página ou menos é um loop infinito.Isso foi relatado como um bug em 2024 , com a mesma expectativa que você — que o comando entrasse em loop. No entanto, foi corrigido na versão 9.6 para que a leitura nesses dispositivos nunca entre em loop.