Intento ambiciosamente traducir un código de C ++ a bash por una miríada de razones.
Este código lee y manipula un tipo de archivo específico para mi subcampo que está escrito y estructurado completamente en binario. Mi primera tarea relacionada con el binario es copiar los primeros 988 bytes del encabezado, exactamente como está, y ponerlos en un archivo de salida en el que pueda continuar escribiendo mientras genero el resto de la información.
Estoy bastante seguro de que mi solución actual no funciona, y de manera realista no he descubierto una buena manera de determinar esto. Entonces, incluso si está escrito correctamente, ¡necesito saber cómo probaría esto para estar seguro!
Esto es lo que estoy haciendo ahora:
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
Si uso hexdump / xxd para revisar esta parte del archivo, aunque no puedo leer exactamente la mayor parte, algo parece estar mal. Y el código que he escrito para comparar solo me dice si dos cadenas son idénticas, no si están copiadas de la forma en que quiero que estén.
¿Hay una mejor manera de hacer esto en bash? ¿Puedo simplemente copiar / leer bytes binarios en binario nativo, para copiarlos en un archivo literalmente? (e idealmente para almacenar como variables también).
dd
para copiar bytes individuales (estableciendo sucount
en1
). Sin embargo, no estoy seguro de almacenarlos.Respuestas:
Tratar con datos binarios a un nivel bajo en scripts de shell es generalmente una mala idea.
bash
las variables no pueden contener el byte 0.zsh
es el único shell que puede almacenar ese byte en sus variables.En cualquier caso, los argumentos de comando y las variables de entorno no pueden contener esos bytes, ya que son cadenas delimitadas por NUL que se pasan a la
execve
llamada del sistema.También tenga en cuenta que:
o su forma moderna:
elimina todos los caracteres de nueva línea finales de la salida de
cmd
. Entonces, si esa salida binaria termina en 0xa bytes, se destruirá cuando se almacene en$var
.Aquí, necesitaría almacenar los datos codificados, por ejemplo con
xxd -p
.Podría definir funciones auxiliares como:
xxd -p
la salida no es eficiente en cuanto al espacio, ya que codifica 1 byte en 2 bytes, pero hace que sea más fácil hacer manipulaciones con ella (concatenación, extracción de partes).base64
es uno que codifica 3 bytes en 4, pero no es tan fácil trabajar con él.El
ksh93
shell tiene un formato de codificación incorporado (usosbase64
) que puede usar con susread
yprintf
/print
utilidades:Ahora, si no hay tránsito a través de variables de shell o env, o argumentos de comando, debe estar bien siempre que las utilidades que use puedan manejar cualquier valor de byte. Pero tenga en cuenta que para las utilidades de texto, la mayoría de las implementaciones que no son GNU no pueden manejar bytes NUL, y querrá arreglar la configuración regional en C para evitar problemas con los caracteres de varios bytes. El último carácter que no es un carácter de nueva línea también puede causar problemas, así como líneas muy largas (secuencias de bytes entre dos bytes 0xa más largos
LINE_MAX
).head -c
donde esté disponible debería estar bien aquí, ya que está destinado a trabajar con bytes y no tiene ninguna razón para tratar los datos como texto. Entoncesdebería estar bien. En la práctica, al menos las implementaciones incorporadas de GNU, FreeBSD y ksh93 están bien. POSIX no especifica la
-c
opción, pero dice quehead
debería admitir líneas de cualquier longitud (no limitado aLINE_MAX
)Con
zsh
:O:
Incluso en el
zsh
caso de que$var
contenga bytes NUL, puede pasarlo como argumento a laszsh
funciones incorporadas (comoprint
arriba) o funciones, pero no como argumentos a los ejecutables, ya que los argumentos pasados a los ejecutables son cadenas delimitadas por NUL, eso es una limitación del núcleo, independiente del shell.fuente
zsh
no es el único shell que puede almacenar uno o más bytes NUL en una variable de shell.ksh93
Puede hacerlo también. Internamente,ksh93
simplemente almacena la variable binaria como una cadena codificada en base64.Bueno, sí. Pero tal vez debería considerar una razón muy importante para NO hacerlo. Básicamente, "bash" / "sh" / "csh" / "ksh" y similares no están diseñados para procesar datos binarios, y tampoco lo son la mayoría de las utilidades estándar de UNIX / LINUX.
Sería mejor quedarse con C ++ o usar un lenguaje de script como Python, Ruby o Perl que sea capaz de manejar datos binarios.
La mejor manera es no hacerlo en bash.
fuente
ffmpeg
,imagemagick
,dd
). Ahora, si uno está programando en lugar de pegar cosas, entonces usar un lenguaje de programación totalmente desarrollado es el camino a seguir.De tu pregunta:
Si está copiando 988 líneas, entonces parece un archivo de texto, no binario. Sin embargo, su código parece asumir 988 bytes, no 988 líneas, por lo que supondré que los bytes son correctos.
Esta parte puede no funcionar. Por un lado, cualquier byte NUL en la secuencia se eliminará, ya que se usa
${hdr_988}
como argumento de línea de comando, y los argumentos de línea de comando no pueden contener NUL. Los backticks también podrían estar haciendo espacios en blanco (no estoy seguro de eso). (En realidad, dado queecho
está integrado, la restricción NUL podría no aplicarse, pero yo diría que todavía es dudosa).¿Por qué no simplemente escribir el encabezado directamente desde el archivo de entrada al archivo de salida, sin pasarlo a través de una variable de shell?
O, más portablemente,
Dado que usted menciona que está utilizando
bash
, no el shell POSIX, tiene disponible la sustitución de procesos, entonces, ¿qué tal esto como prueba?Finalmente: considere usar en
$( ... )
lugar de backticks.fuente
dd
no es necesariamente equivalente ahead
para archivos no regulares.head
hará tantasread(2)
llamadas al sistema como sea necesario para obtener esos 988 bytes, mientrasdd
que solo hará unaread(2)
. GNUdd
tieneiflag=fullblock
que probar y leer ese bloque completo, pero eso es aún menos portátil quehead -c
.