bash no puede almacenar el valor hexadecimal 0x00 en variable

11

Estoy tratando de hacer algunos trucos con dd. Pensé que sería posible almacenar algunos valores hexadecimales en una variable llamada "encabezado" para canalizarlo en dd.

Mi primer paso sin una variable fue este:

$ echo -ne "\x36\xc9\xda\x00\xb4" |dd of=hex
$ hd hex

00000000  36 c9 da 00 b4                                    |6....|
00000005

Después de eso probé esto:

$ header=$(echo -ne "\x36\xc9\xda\x00\xb4") 
$ echo -n $header | hd

00000000  36 c9 da b4                                       |6...|
00000004

Como puede ver, perdí mi \x00valor en la $headervariable. ¿Alguien tiene una explicación para este comportamiento? Esto me está volviendo loco.

Franco
fuente
Consigo bash: warning: command substitution: ignored null byte in input.
Kusalananda
Le faltan comillas, debería ser, header="$(echo -ne "\x36\xc9\xda\x00\xb4")"; echo -n "$header" | hdsin embargo, esto solo da el mismo resultado.
ctrl-alt-delor
Esto funciona header="\x36\xc9\xda\x00\xb4"; echo -n "$header" | hd, pero no es lo mismo que almacenar la forma legible por humanos.
ctrl-alt-delor

Respuestas:

16

No puede almacenar un byte nulo en una cadena porque Bash usa cadenas de estilo C, que reservan el byte nulo para los terminadores. Por lo tanto, debe volver a escribir su script para simplemente canalizar la secuencia que contiene el byte nulo sin que Bash necesite almacenarlo en el medio. Por ejemplo, puedes hacer esto:

printf "\x36\xc9\xda\x00\xb4" | hd

Tenga en cuenta, por cierto, que no necesita echo; puede usar Bash printfpara esto y muchas otras tareas simples.

O en lugar de encadenar, puede usar un archivo temporal:

printf "\x36\xc9\xda\x00\xb4" > /tmp/mysequence
hd /tmp/mysequence

Por supuesto, esto tiene el problema de que el archivo /tmp/mysequenceya puede existir. Y ahora necesita seguir creando archivos temporales y guardar sus rutas en cadenas.

O puede evitar eso utilizando la sustitución del proceso:

hd <(printf "\x36\xc9\xda\x00\xb4")

El <(command)operador crea una tubería con nombre en el sistema de archivos, que recibirá la salida de command. hdrecibirá, como primer argumento, la ruta a esa tubería, que abrirá y leerá casi como cualquier archivo. Puede leer más sobre esto aquí: /unix//a/17117/136742 .

giusti
fuente
1
Si bien es correcto, este es un detalle de implementación y no la razón exacta. Lo miré y el estándar POSIX realmente requiere este comportamiento, por lo que tiene la razón real . (Como algunos han señalado, zshlo haré, pero solo en el modo nōn-POSIX.) En realidad lo mksh
investigué
@mirabilos, ¿te gustaría ampliar eso? AFAICT, el comportamiento no está especificado por POSIX para la sustitución de comandos cuando la salida tiene caracteres NUL, y para zsh en modo POSIX, la única diferencia relevante que puedo pensar es que en la shemulación, \0no está en el valor predeterminado de $IFS. echo "$(printf 'a\0b')"todavía funciona bien en shemulación en zsh.
Stéphane Chazelas
3
@mirabilos Teniendo en cuenta que los shells son anteriores al estándar POSIX en una década o más, supongo que podría descubrir que la razón real real es que los shells usaron cadenas de estilo C y el estándar se construyó alrededor de eso.
giusti
Encontré una buena Q para una discusión detallada sobre printf versus echo. unix.stackexchange.com/questions/65803/…
Paulb
8

En su zshlugar, puede usar cuál es el único shell que puede almacenar el carácter NUL en sus variables. Ese personaje incluso está en el valor predeterminado de $IFSin zsh.

nul=$'\0'

O:

nul=$'\x0'

O

nul=$'\u0000'

O

nul=$(printf '\0')

Sin embargo, tenga en cuenta que no puede pasar una variable como argumento o variable de entorno a un comando que se ejecuta ya que los argumentos y las variables de entorno son cadenas delimitadas por NUL que se pasan a la execve()llamada del sistema (una limitación de la API del sistema, no el shell ) En zsh, sin embargo, puede pasar bytes NUL como argumentos a funciones o comandos incorporados.

echo $'\0' # works
/bin/echo $'\0' # doesn't
Stéphane Chazelas
fuente
1
"Puedes usar zsh en su lugar". No, gracias. Me estoy enseñando bash-scripting como principiante en este momento. No quiero confundirme con otra sintaxis. Pero muchas gracias por sugerirlo
Frank
De hecho, usaste la zshsintaxis en tu pregunta. echo -n $headersignifica pasar el contenido de la $headervariable como último argumento a echo -nes zsh(o fisho rco es) sintaxis, no bash sintaxis. En bash, eso tiene un significado muy diferente . Más generalmente zshes como ksh( bashel shell GNU, que es más o menos un clon parcial del kshshell Unix de facto) pero con la mayoría de las idiosincrasias de diseño del shell Bourne fijadas (y muchas características adicionales y muchas más fácil de usar / menos sorprendente).
Stéphane Chazelas
Tenga cuidado: zsh puede cambiar un byte cero a veces: echo $(printf 'ab\0cd') | od -vAn -tx1c imprime `61 62 20 63 64 0a`, ese es un espacio donde debería existir un NUL.
Isaac
1
Y eso es algo que ningún otro shell (ninguno, nulo) reproducirá. Eso hace que un script se comporte de maneras muy especiales en zsh. En mi opinión: zsh solo está tratando de ser demasiado inteligente.
Isaac
2
Habiendo "arreglado" las fallas de diseño presentes en el estándar POSIX sh de que acostumbrarse a escribir scripts zsh significa que uno se está acostumbrando a prácticas que serían defectuosas si se ejercitaran en cualquier otro shell. Este no es un problema con una sintaxis que es tan diferente a un idioma diferente que las habilidades o hábitos probablemente no se transfieran, pero ese no es el caso en cuestión.
Charles Duffy