Eu tenho um script Bash que acrescenta bytes escritos como valores hexadecimais. Eu uso echo
para escrever os bytes e funciona
hex="1F"
byte="\x$hex"
echo -en $byte >> "output.bin"
Em algum momento, preciso preencher o arquivo com um byte que pode ser qualquer coisa de 00
a FF
. Quero especificar o byte como um valor hexadecimal e o total de repetições.
Tentei fazer isso com um for
loop, mas demora muito, principalmente porque às vezes preciso adicionar algo como 65.535 bytes.
byte="00"
total=65515
for (( i=1; i<=total; i++ ));
do
echo -en "\x$byte" >> "output.bin"
done
Estou procurando uma maneira mais performática que não use um for
loop. No momento, estou preso com algo entre printf
e, echo
mas em vez de escrever os valores como binários, ele os escreve como texto
result=$(eval printf "\x$byte%0.s" {1..$total})
echo -en $result >> "output.bin"
O resultado no arquivo de saída é 78 30 30
, que é o texto "x00", em vez de 00
. Se eu usar echo -en "\x00" >> "output.bin"
, ele escreve um byte contendo o 00
valor e não o texto "x00". É aqui que não sei como proceder para fazê-lo escrever o valor binário real
Você está executando essa substituição de comando com
eval
, então as aspas e escapes são processados duas vezes.Supondo que
byte=41 total=3
, o queeval
vê são os argumentosprintf
,\x41%0.s
,1
,2
,3
. Ele os concatena aprintf \x41%0.s 1 2 3
, que após o processamento de escape é executadoprintf
com os quatro argumentosx41%0.s
,1
,2
,3
. (Ele deve imprimirx41x41x41
, aqui.)Você pode corrigir isso adicionando a quantidade correta de barras invertidas, mas provavelmente também não deve usar
echo -e
, pois isso só bagunçaria o resultado se você estivesse imprimindo barras invertidas.De qualquer forma,
A menos que você queira fazer isso do jeito mais difícil de propósito, seria mais fácil usar outras ferramentas do que apenas o shell. Por exemplo, você poderia usar
tr
para obter o byte que deseja, mas ele não suporta escapes hexadecimais, apenas octal com\000
, então há o problema de conversão.Ou você pode usar Perl:
Observe que, se for sobre preenchimento com 0 bytes para alinhar a algum limite, você pode usar
dd
withconv=sync
para isso.(
iflag=fullblock
é uma extensão GNU¹, mas é necessária apenas para entradas de pipe ou, de forma mais geral, quando pode haver leituras curtas, não para arquivos regulares; com GNUdd
, você também pode adicionarstatus=none
para remover as informações de transferência).Ou você pode usar
truncate
um arquivo já existente em um tamanho maior, para que o final seja preenchido com NULs (não ocupando mais espaço em disco):Ou o mesmo em
fallocate -l
vez detruncate -s
alocar espaço em disco.Observe que entre os shells que copiaram
zsh
's{x..y}
,bash
é o único que{1..$n}
não funciona, então aqui, você também pode alternar para um desses shells (zsh
,ksh93
,yash -o brace-expand
).Em zsh:
zsh também tem operadores de preenchimento e (ao contrário de
bash
) pode armazenar bytes arbitrários em sua variável:(cuidado, o preenchimento é baseado no número de caracteres, use
set +o multibyte
-o por byte em locais onde os caracteres podem ser compostos de mais de um byte).¹ Adicionado à edição de 2024 do padrão POSIX , mas atualmente com um erro de digitação infeliz , pois ,
iflags=fullblock
em vez deiflag=fullblock
, eles copiaram o erro de digitação da solicitação para adicionar essa extensão GNU ao padrão .GNU/awk tem uma função para converter expressões hexadecimais em um caractere. Ele também criará uma string longa (usando uma opção length para sprintf) e, então, substituirá globalmente seu caractere.
Ele produz 70 KB em cerca de 1/20 de segundo.