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).