Estou ambiciosamente tentando traduzir um código c ++ no bash por uma infinidade de razões.
Este código lê e manipula um tipo de arquivo específico para o meu subcampo que é escrito e estruturado completamente em binário. Minha primeira tarefa relacionada ao binário é copiar os primeiros 988 bytes do cabeçalho, exatamente como estão, e colocá-los em um arquivo de saída no qual eu possa continuar gravando enquanto gere o restante das informações.
Tenho certeza de que minha solução atual não está funcionando e, realisticamente, não descobri uma boa maneira de determinar isso. Portanto, mesmo que esteja realmente escrito corretamente, preciso saber como testaria isso para ter certeza!
Isto é o que estou fazendo agora:
hdr_988=`head -c 988 ${inputFile}`
echo -n "${hdr_988}" > ${output_hdr}
headInput=`head -c 988 ${inputTrack} | hexdump`
headOutput=`head -c 988 ${output_hdr} | hexdump`
if [ "${headInput}" != "${headOutput}" ]; then echo "output header was not written properly. exiting. please troubleshoot."; exit 1; fi
Se eu usar o hexdump / xxd para verificar esta parte do arquivo, embora não possa ler exatamente a maior parte, algo parece errado. E o código em que escrevi para comparação apenas me diz se duas strings são idênticas, não se elas são copiadas da maneira que eu quero que elas sejam.
Existe uma maneira melhor de fazer isso no bash? Posso simplesmente copiar / ler bytes binários no binário nativo, para copiar para um arquivo literalmente? (e, idealmente, para armazenar também como variáveis).
dd
para copiar bytes individuais (configurandocount
como1
). Eu não tenho certeza sobre armazená-los, no entanto.Respostas:
Lidar com dados binários em um nível baixo em scripts de shell geralmente é uma má idéia.
bash
As variáveis não podem conter o byte 0.zsh
é o único shell que pode armazenar esse byte em suas variáveis.Em qualquer caso, argumentos de comando e variáveis de ambiente não podem conter esses bytes, pois são cadeias delimitadas por NUL passadas para a
execve
chamada do sistema.Observe também que:
ou sua forma moderna:
retira todos os caracteres de nova linha à direita da saída de
cmd
. Portanto, se essa saída binária terminar em 0xa bytes, ela será mutilada quando armazenada em$var
.Aqui, você precisará armazenar os dados codificados, por exemplo, com
xxd -p
.Você pode definir funções auxiliares como:
xxd -p
a saída não é eficiente em termos de espaço, pois codifica 1 byte em 2 bytes, mas facilita a manipulação (concatenação, extração de peças).base64
é aquele que codifica 3 bytes em 4, mas não é tão fácil de trabalhar.O
ksh93
shell possui um formato de codificação interno (usosbase64
) que você pode usar com seusread
eprintf
/print
utilitários:Agora, se não houver trânsito por meio de variáveis shell ou env, ou argumentos de comando, você deve ficar bem desde que os utilitários que você usa possam lidar com qualquer valor de byte. Mas observe que para utilitários de texto, a maioria das implementações não-GNU não pode manipular NUL bytes, e você deseja corrigir o código do idioma em C para evitar problemas com caracteres de vários bytes. O último caractere que não é um caractere de nova linha também pode causar problemas e linhas muito longas (sequências de bytes entre dois bytes de 0xa maiores que esse
LINE_MAX
).head -c
onde está disponível, deve estar OK aqui, pois deve funcionar com bytes e não tem motivos para tratar os dados como texto. entãodeve estar ok. Na prática, pelo menos as implementações internas GNU, FreeBSD e ksh93 estão OK. O POSIX não especifica a
-c
opção, mas diz quehead
deve suportar linhas de qualquer comprimento (não se limitando aLINE_MAX
)Com
zsh
:Ou:
Mesmo que
zsh
, se$var
contenha NUL bytes, você pode passá-lo como argumento parazsh
builtins (comoprint
acima) ou funções, mas não como argumentos para executáveis, pois os argumentos passados para executáveis são seqüências delimitadas por NUL, que é uma limitação do kernel, independente do shell.fonte
zsh
não é o único shell que pode armazenar um ou mais bytes NUL em uma variável do shell.ksh93
pode fazer isso também. Internamente,ksh93
simplesmente armazena a variável binária como uma sequência codificada em base64.Bem, sim. Mas talvez você deva considerar uma razão muito importante para NÃO fazer isso. Basicamente, "bash" / "sh" / "csh" / "ksh" e similares não são projetados para o processamento de dados binários, nem a maioria dos utilitários padrão do UNIX / LINUX.
É melhor você ficar com o C ++ ou usar uma linguagem de script como Python, Ruby ou Perl que seja capaz de lidar com dados binários.
A melhor maneira é não fazê-lo no bash.
fonte
ffmpeg
,imagemagick
,dd
). Agora, se você está fazendo programação, em vez de colar coisas, usar uma linguagem de programação com potência total é o caminho a percorrer.Da sua pergunta:
Se você estiver copiando 988 linhas, parece um arquivo de texto, não binário. No entanto, seu código parece assumir 988 bytes, não 988 linhas, portanto, assumirei que os bytes estão corretos.
Esta parte pode não funcionar. Por um lado, quaisquer bytes NUL no fluxo serão removidos, porque você usa
${hdr_988}
como argumento de linha de comando, e os argumentos de linha de comando não podem conter NUL. Os backticks também podem estar mudando de espaço em branco (não tenho certeza disso). (Na verdade, comoecho
é um componente interno, a restrição NUL pode não se aplicar, mas eu diria que ainda é duvidoso.)Por que não escrever o cabeçalho diretamente do arquivo de entrada no arquivo de saída, sem passar por uma variável de shell?
Ou, de maneira mais portável,
Como você menciona que está usando
bash
, e não o shell POSIX, você tem a substituição de processo disponível para você, então que tal isso como teste?Finalmente: considere usar em
$( ... )
vez de reticulares.fonte
dd
não é necessariamente equivalente ahead
arquivos não regulares.head
fará quantasread(2)
chamadas de sistema forem necessárias para obter esses 988 bytes, enquantodd
fará apenas umaread(2)
. O GNUdd
precisaiflag=fullblock
tentar ler esse bloco na íntegra, mas isso é ainda menos portátil quehead -c
.