Qual é a maneira POSIX de ler um número exato de bytes de um arquivo?

2

Basta acertar esse problema e aprendeu muito com a resposta escolhida: Crie dados aleatórios com dd e obtenha" aviso parcial de leitura ". Os dados após o aviso agora são realmente aleatórios?

Infelizmente, a solução sugerida head -c não é portátil.

Para as pessoas que insistem em que dd é a resposta, leia atentamente a resposta vinculada, o que explica em detalhes por que dd não pode ser a resposta. Além disso, por favor, observe isto:

$ dd bs=1000000 count=10 if=/dev/random of=random
dd: warning: partial read (89 bytes); suggest iflag=fullblock
0+10 records in
0+10 records out
143 bytes (143 B) copied, 99.3918 s, 0.0 kB/s
$ ls -l random ; du -kP random
-rw-rw-r-- 1 me me 143 Apr 22 19:19 random
4       random
$ pwd
/tmp
    
por Low Powah 22.04.2016 / 23:23

2 respostas

6

Infelizmente, para manipular o conteúdo de um arquivo binário, dd é praticamente a única ferramenta no POSIX. Embora a maioria das implementações modernas de ferramentas de processamento de texto ( cat , sed , awk ,…) possam manipular arquivos binários, isso não é exigido pelo POSIX: algumas implementações mais antigas se sufocam em bytes nulos, entrada não terminada por uma nova linha ou seqüências de bytes inválidos na codificação de caracteres do ambiente.

É possível, mas difícil, usar dd com segurança. A razão de eu gastar muita energia afastando as pessoas é que há muitos conselhos por aí que promovem dd em situações em que isso não é útil nem seguro.

O problema com dd é sua noção de blocos: ele pressupõe que uma chamada para read retorna um bloco; Se read retornar menos dados, você receberá um bloqueio parcial, que gera itens como skip e count off. Veja um exemplo que ilustra o problema, em que dd está lendo de um canal que entrega dados de maneira relativamente lenta:

yes hello | while read line; do echo $line; done | dd ibs=4 count=1000 | wc -c

Em um Linux padrão (Debian jessie, kernel Linux 3.16, dd do GNU coreutils 8.23), eu obtenho um número altamente variável de bytes, variando de cerca de 3000 a quase 4000. Mude o tamanho do bloco de entrada para um divisor de 6, e a saída é consistentemente 4000 bytes como seria ingenuamente esperada - a entrada para dd chega em rajadas de 6 bytes, e contanto que um bloco não abranja várias rajadas, dd começa a ler um bloco completo.

Isso sugere uma solução: use um tamanho de bloco de entrada de 1 . Não importa como a entrada é produzida, não há como dd ler um bloco parcial se o tamanho do bloco de entrada for 1. (Isso não é totalmente óbvio: dd pode ler um bloco de tamanho 0 se for interrompido por um signal - mas se for interrompido por um sinal, a chamada do sistema read retornará -1. Um read retornando 0 só será possível se o arquivo for aberto no modo sem bloqueio e, nesse caso, um read terá melhor não deve ser considerado como tendo sido executado de forma alguma.No modo de bloqueio, read só retorna 0 no final do arquivo.)

dd ibs=1 count="$number_of_bytes"

O problema com essa abordagem é que ela pode ser lenta (mas não chocantemente lenta: apenas cerca de 4 vezes mais lenta que head -c no meu benchmark rápido).

O POSIX define outras ferramentas que leem dados binários e os convertem em um formato de texto: uuencode (saídas no formato histórico do uuencode ou no Base64), od (produz uma saída octal ou dump hexadecimal). Nenhum deles é adequado para a tarefa em mãos. uuencode pode ser desfeito por uudecode , mas a contagem de bytes na saída é desajeitada porque o número de bytes por linha de saída não é padronizado. É possível obter uma saída bem definida de od , mas infelizmente não há nenhuma ferramenta POSIX para fazer o contrário (isso pode ser feito, mas apenas através de loops lentos em sh ou awk, o que anula o propósito aqui).

    
por 23.04.2016 / 01:36
-1

Parte do ponto de usar dd é que o usuário escolhe o tamanho de bloco que usa. Se dd falhar por tamanhos de bloco muito grandes, IMO é responsabilidade do usuário tentar tamanhos de bloco menores. Eu poderia pedir uma TB de dd em um bloco, mas isso não significa que eu vou conseguir.

Se você quiser um número exato de bytes, isso será terrivelmente lento, mas deve funcionar:

dd bs=1 count=1000000

Se até mesmo um tamanho de bloco de 1 resultar em leituras parciais,…

    
por 23.04.2016 / 01:29