
Porque eso es lo que algunos de ellos están haciendo.
> echo echo Hallo, Baby! | iconv -f utf-8 -t utf-16le > /tmp/hallo
> chmod 755 /tmp/hallo
> dash /tmp/hallo
Hallo, Baby!
> bash /tmp/hallo
/tmp/hallo: /tmp/hallo: cannot execute binary file
> (echo '#'; echo echo Hallo, Baby! | iconv -f utf-8 -t utf-16le) > /tmp/hallo
> bash /tmp/hallo
Hallo, Baby!
> mksh /tmp/hallo
Hallo, Baby!
> cat -v /tmp/hallo
#
e^@c^@h^@o^@ ^@H^@a^@l^@l^@o^@,^@ ^@B^@a^@b^@y^@!^@
^@
¿Es esto alguna molestia de compatibilidad en realidad?requeridopor el estándar? Porque parece bastante peligroso e inesperado.
Respuesta1
segúnPOSIX,
El archivo de entrada será un archivo de texto, excepto que la longitud de las líneas será ilimitada.¹
Caracteres NUL² en la entradahazlo sin texto, por lo que el comportamiento no está especificado en lo que respecta a POSIX, por lo que sh
las implementaciones pueden hacer lo que quieran (y un compatible con POSIXguionno debe contener NUL).
Hay algunos shells que escanean los primeros bytes en busca de 0 y se niegan a ejecutar el script asumiendo que intentó ejecutar un archivo que no es un script por error.
Esto es útil porque las exec*p()
funciones, env
comandos sh
, find -exec
... sonrequeridollamar a un shell para interpretar un comando si el sistema regresa con ENOEXEC execve()
, por lo tanto, si intenta ejecutar un comando para la arquitectura incorrecta, es mejor obtener unno ejecutará un binarioerror de archivo desde su shell que el shell tratando de darle sentido como un script de shell.
Eso está permitido por POSIX:
Si el archivo ejecutable no es un archivo de texto, el shell puede omitir la ejecución de este comando.
Que en la próxima revisión de la normaserá cambiado a:
El shell puede aplicar una verificación heurística para determinar si el archivo a ejecutar podría ser un script y puede omitir la ejecución de este comando si determina que el archivo no puede ser un script. En este caso, escribirá un mensaje de error y devolverá un estado de salida de 126.
Nota: una heurística común para rechazar archivos que no pueden ser un script es ubicar un byte NUL antes de un byte <nueva línea> dentro de un archivo de longitud fija. prefijo del archivo. Dado que se requiere que sh acepte archivos de entrada con longitudes de línea ilimitadas, la verificación heurística no puede basarse en la longitud de la línea.
Ese comportamiento puede interferir con los archivos autoextraíbles del shell que contienen un encabezado de shell seguido de datos binarios¹.
El zsh
shell admite NUL en su entrada, aunque tenga en cuenta que los NUL no se pueden pasar en los argumentos de execve()
, por lo que solo puede usarlos en el argumento o en los nombres deincorporadocomandos o funciones:
$ printf '\0() echo zero; \0\necho \0\n' | zsh | hd
00000000 7a 65 72 6f 0a 00 0a |zero...|
00000007
(aquí definiendo y llamando a una función con NUL como nombre y pasando un carácter NUL como argumento al echo
comando incorporado).
Algunos los despojarán, lo cual también es algo sensato. NUL
A veces se utilizan como relleno. Por ejemplo, los terminales los ignoran (a veces se enviaban a los terminales para darles tiempo para procesar secuencias de control complejas (como el retorno de carro (literalmente)). Los agujeros en los archivos aparecen llenos de NUL, etc.
Tenga en cuenta que el contenido no textual no se limita a bytes NUL. También es una secuencia de bytes que no forman caracteres válidos en la configuración regional. Por ejemplo, el valor del byte 0xc1 no puede aparecer en texto codificado en UTF-8. Entonces, en configuraciones regionales que usan UTF-8 como codificación de caracteres, un archivo que contiene dicho byte no es un archivo de texto válido y, por lo tanto, no es un sh
script³ válido.
En la práctica, yash
es el único shell que conozco que se quejará de una entrada no válida.
¹ En la próxima revisión de la norma,va a cambiara
El archivo de entrada puede ser de cualquier tipo, pero la parte inicial del archivo que se pretende analizar de acuerdo con la gramática del shell (XREF a XSH 2.10.2 Reglas gramaticales del shell) estará compuesta por caracteres y no contendrá el carácter NUL. El caparazón no impondrá ningún límite de longitud de línea.
requerir explícitamente que los shells admitan entradas que comiencen con una sección sintácticamente válida sin bytes NUL, incluso si el resto contiene NUL, para dar cuenta de los archivos autoextraíbles.
² y los caracteres deben descodificarse según la codificación de caracteres de la configuración regional (consulte el resultado de locale charmap
), y en el sistema POSIX, el carácter NUL (cuya codificación es siempre el byte 0) es el único carácter cuya codificación contiene el byte 0. En otros En otras palabras, UTF-16 no se encuentra entre las codificaciones de caracteres que se pueden utilizar en una configuración regional POSIX.
³ Sin embargo , existe la cuestión del cambio de configuración regional dentro del script (como cuando se asignan las variables ///) y en qué punto el cambio surte efecto para el shell que interpreta la entrada LANG
.LC_CTYPE
LC_ALL
LOCPATH
Respuesta2
La razón de este comportamiento es un poco compleja...
Primero, los shells modernos incluyen una verificación de archivos potencialmente binarios (que contienen bytes nulos), pero esta verificación solo verifica la primera línea del archivo. Es por eso que el '#' en la primera línea cambia el comportamiento. El Bourne Shell histórico no tiene esa verificación binaria y ni siquiera necesita el '#' para comportarse de la manera que mencionaste.
Luego, el método específico utilizado por Bourne Shell para admitir caracteres de varios bytes mbtowc()
simplemente omite todos los bytes nulos porque mbtowc()
devuelve la longitud del carácter 0 para un byte nulo y esto provoca que un bucle vuelva a intentar el siguiente carácter.
Bourne Shell introdujo este tipo de código alrededor de 1988 y es posible que otros shells copiaran el comportamiento.