Bash utiliza cadenas de estilo C internamente, que terminan en bytes nulos. Esto significa que una cadena Bash (como el valor de una variable o un argumento para un comando) nunca puede contener un byte nulo. Por ejemplo, este mini script:
foobar=$'foo\0bar' # foobar='foo' + null byte + 'bar'
echo "${#foobar}" # print length of $foobar
en realidad imprime 3
, porque en $foobar
realidad es solo 'foo'
: bar
viene después del final de la cadena.
Del mismo modo, echo $'foo\0bar'
solo imprime foo
, porque echo
no sabe sobre la \0bar
pieza.
Como puede ver, la \0
secuencia en realidad es muy engañosa en una $'...'
cadena de estilo; parece un byte nulo dentro de la cadena, pero no termina funcionando de esa manera. En su primer ejemplo, su read
comando tiene -d $'\0'
. ¡Esto funciona, pero solo porque -d ''
también funciona! (Esa no es una característica explícitamente documentada de read
, pero supongo que funciona por la misma razón: ''
es la cadena vacía, por lo que su byte nulo de terminación aparece de inmediato. Está documentado como "El primer carácter de delim ", y supongo que incluso funciona si el "primer carácter" ha pasado el final de la cadena!)-d delim
Pero como sabe por su find
ejemplo, es posible que un comando imprima un byte nulo y que ese byte se canalice a otro comando que lo lea como entrada. Ninguna parte de eso se basa en almacenar un byte nulo en una cadena dentro de Bash . El único problema con su segundo ejemplo es que no podemos usarlo $'\0'
en un argumento para un comando; echo "$file"$'\0'
podría imprimir felizmente el byte nulo al final, si supiera que lo desea.
Entonces, en lugar de usar echo
, puede usar printf
, que admite los mismos tipos de secuencias de escape que $'...'
las cadenas de estilo. De esa manera, puede imprimir un byte nulo sin tener que tener un byte nulo dentro de una cadena. Eso se vería así:
for file in * ; do printf '%s\0' "$file" ; done \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
o simplemente esto:
printf '%s\0' * \
| while IFS= read -r -d '' ; do echo "$REPLY" ; done
(Nota: en echo
realidad también tiene una -e
bandera que le permitiría procesar \0
e imprimir un byte nulo; pero luego también trataría de procesar cualquier secuencia especial en su nombre de archivo. Por lo tanto, el printf
enfoque es más robusto).
Por cierto, hay algunas conchas que no permiten nula bytes de cuerdas dentro. Su ejemplo funciona bien en Zsh, por ejemplo (asumiendo la configuración predeterminada). Sin embargo, independientemente de su shell, los sistemas operativos tipo Unix no proporcionan una forma de incluir bytes nulos dentro de los argumentos a los programas (ya que los argumentos del programa se pasan como cadenas de estilo C), por lo que siempre habrá algunas limitaciones. (Su ejemplo puede funcionar en Zsh solo porque echo
es un shell integrado, por lo que Zsh puede invocarlo sin depender del soporte del sistema operativo para invocar otros programas. Si usó en command echo
lugar de echo
, de modo que omitió el builtin y utilizó el echo
programa independiente en el $PATH
, verías el mismo comportamiento en Zsh que en Bash).
-d ''
ya significa delimitar\0
? Encontré una explicación aquí: stackoverflow.com/questions/8677546/…