¿Por qué el comportamiento de la sintaxis `#!` No está especificado por POSIX?

17

Desde la página del lenguaje de comandos de Shell de la especificación POSIX:

Si la primera línea de un archivo de comandos de shell comienza con los caracteres "#!", Los resultados no se especifican.

¿Por qué el comportamiento de #!no especificado por POSIX? Me resulta desconcertante que algo tan portátil y ampliamente utilizado tenga un comportamiento no especificado.

Harold Fischer
fuente
1
Los estándares dejan cosas sin especificar para no vincular implementaciones a comportamientos particulares. Por ejemplo, un "inicio de sesión" es "La actividad no especificada por la cual un usuario obtiene acceso al sistema".
Kusalananda
2
Dado que POSIX no especifica rutas ejecutables, una línea shebang es inherentemente no portátil de todos modos; No estoy seguro de que se ganaría mucho al especificarlo independientemente.
Michael Homer
1
@MichaelHomer, ¿no? El estándar podría especificar que la línea contiene una ruta para el intérprete, incluso sin decir cuál debería ser esa ruta.
ilkkachu
1
@HaroldFischer Excepto que no es interpretado por el shell, es interpretado por el núcleo del sistema operativo (hecho al menos en Linux, que en realidad puede deshabilitar este soporte durante el tiempo de compilación) o cualquier biblioteca que implemente la exec()función. Por lo tanto, verificar contra múltiples proyectiles realmente no le dice qué tan portátil es.
Austin Hemmelgarn
2
@HaroldFischer Además, incluso entre los sistemas operativos compatibles con POSIX, el comportamiento no es consistente. Linux y macOS se comportan de manera diferente: Linux no tokeniza completamente la línea shebang por espacios. macOS no permite que el intérprete de guiones sea otro guión. Ver también en.wikipedia.org/wiki/Shebang_(Unix)#Portability
jamesdlin

Respuestas:

21

Pienso principalmente porque:

  • El comportamiento varía mucho entre la implementación. Ver https://www.in-ulm.de/~mascheck/various/shebang/ para todos los detalles.

    Sin embargo, ahora podría especificar un subconjunto mínimo de la mayoría de las implementaciones tipo Unix: como #! *[^ ]+( +[^ ]+)?\n (con solo caracteres del conjunto de caracteres de nombre de archivo portátil en esas una o dos palabras) donde la primera palabra es una ruta absoluta a un ejecutable nativo, la cosa no es demasiado largo y comportamiento no especificado si el ejecutable es setuid / setgid, y la implementación definió si la ruta del intérprete o la ruta del script se pasa argv[0]al intérprete.

  • POSIX no especifica la ruta de los ejecutables de todos modos. Varios sistemas tienen utilidades pre-POSIX en /bin/ /usr/biny tienen las utilidades POSIX en otro lugar (como en Solaris 10 donde /bin/shhay un shell Bourne y el POSIX está en/usr/xpg4/bin ; Solaris 11 lo reemplazó con ksh93 que es más compatible con POSIX, pero la mayoría de los otros las herramientas /bintodavía son antiguas y no POSIX). Algunos sistemas no son POSIX pero tienen un modo / emulación POSIX. Todo lo que requiere POSIX es que haya un entorno documentado en el que un sistema se comporte POSIXly.

    Ver Windows + Cygwin por ejemplo. En realidad, con Windows + Cygwin, el she-bang se honra cuando una aplicación cygwin invoca un script, pero no una aplicación nativa de Windows.

    Entonces, incluso si POSIX especificó el mecanismo shebang, no podría usarse para escribir scripts POSIX sh/ sed/ awk... (también tenga en cuenta que el mecanismo shebang no puede usarse para escribir confiable sed/awk scripts ya que no permite pasar un fin de opción marcador).

Ahora, el hecho de que no esté especificado no significa que no pueda usarlo (bueno, dice que no debe comenzar la primera línea con #! si esperas que sea solo un comentario regular y no una explosión), pero POSIX no le garantiza si lo hace.

En mi experiencia, el uso de shebangs le brinda más garantía de portabilidad que el uso de la forma en que POSIX escribe scripts de shell: omita she-bang, escriba el script en shsintaxis POSIX y espere que cualquier cosa que invoque el script invoque un POSIX compatible shcon él, que es bien si sabe que la herramienta correcta invocará el script en el entorno correcto, pero no de otra manera.

Puede que tenga que hacer cosas como:

#! /bin/sh -
if : ^ false; then : fine, POSIX system by default
else
  # cover Solaris 10 or older. ": ^ false" returns false
  # in the Bourne shell as ^ is an alias for | there for
  # compatibility with the Thomson shell.
  PATH=`getconf PATH`:$PATH; export PATH
  exec /usr/xpg4/bin/sh - "$0" ${1+"$@"}
fi
# rest of script

Si quieres ser portátil a Windows + Cygwin, es posible que tenga el nombre de su archivo con una .bato .ps1extensión y utilizar algún truco similar para cmd.exeo powershell.exepara invocar el cygwin shen el mismo archivo.

Stéphane Chazelas
fuente
Curiosamente, del número 5 : "La construcción #! Está reservada para implementaciones que deseen proporcionar esa extensión. Una aplicación portátil no puede usar #! Como la primera línea de un script de shell; puede que no se interprete como un comentario".
muru
@muru Si la secuencia de comandos fuera verdaderamente portátil, en un sistema POSIX que ejecutara POSIX sh, no necesitaría una línea hashbang, ya que POSIX la ejecutaría sh.
Kusalananda
1
@Kusalananda eso solo es cierto si se usara execlpo execvpno, ¿verdad? Si execvetuviera que usar , ¿resultaría en ENOEXEC?
muru
9

[E] l comportamiento parece coherente entre todos los shells de denuncia POSIX. No veo la necesidad de la necesidad de margen de maniobra aquí.

No estás mirando lo suficientemente profundo.

En la década de 1980, este mecanismo fue no estandarizado de facto. Aunque Dennis Ritchie lo había implementado, esa implementación no había llegado al público en el lado AT&T del universo. Efectivamente, solo estaba disponible públicamente y se conocía en BSD; con scripts de shell ejecutables no disponibles en AT&T Unix. Por lo tanto, no era razonable estandarizarlo. El estado de cosas está ejemplificado por este doco contemporáneo, uno de muchos de ellos:

Tenga en cuenta que BSD permite que los archivos que comienzan #! interpreterse ejecuten directamente, mientras que SysV permite que solo se ejecuten directamente los archivos a.out. Esto significa que una instancia de una de las exec…()rutinas en un programa BSD puede tener que cambiarse bajo SysV para ejecutar el intérprete (típicamente/bin/sh ) para ese programa.
- Stephen Frede (1988). "Programación en el sistema X Release Y". Boletín del grupo de usuarios de Australia Unix Systems . Volumen 9. Número 4. p. 111)

Un punto importante aquí es que está mirando shells, mientras que la existencia de scripts de shell ejecutables es realmente un asunto de las exec…()funciones. Lo que hacen los shells incluye los precursores del mecanismo de script ejecutable, que todavía se encuentra en algunos shells aún hoy (y también hoy en día es obligatorio para el exec…p()subconjunto de funciones), y es algo engañoso. Lo que el estándar debe abordar a este respecto es cómo funciona exec…()un script interpretado, y en el momento en que POSIX se creó originalmente , simplemente no funcionó en primer lugar en una parte importante del espectro de los sistemas operativos de destino .

Una pregunta subordinada es por qué esto no se ha estandarizado desde entonces, especialmente porque el mecanismo del número mágico para los intérpretes de guiones había llegado al público en el lado AT&T del universo y se había documentado exec…()en la Definición de interfaz del Sistema 5 , a fines de la década de 1990 :

Un archivo de intérprete comienza con una línea del formulario.

#! nombre de ruta [arg]
donde pathname es la ruta del intérprete y arg es un argumento opcional. Cuando se exectrata de un archivo de intérprete, el sistema execes el intérprete especificado.
- exec. Definición V System Interface . Volumen 1. 1991.

Desafortunadamente, el comportamiento sigue siendo hoy casi tan divergente como lo era en la década de 1980 y no hay un comportamiento verdaderamente común para estandarizar. Algunos Unices (famosos HP-UX y FreeBSD, por ejemplo) no admiten scripts como intérpretes para scripts. Si la primera línea es uno, dos o muchos elementos separados por espacios en blanco varía entre MacOS (y las versiones de FreeBSD anteriores a 2005) y otros. La longitud máxima de ruta admitida varía. y los caracteres que se encuentran fuera del juego de caracteres de nombre de archivo portátil POSIX son complicados, al igual que los espacios en blanco iniciales y finales. Lo que terminan siendo el 0º, 1º y 2º argumento también es complicado, con una variación significativa entre los sistemas. Algunos actualmente son compatibles con POSIX pero no-Los sistemas Unix todavía no admiten ningún mecanismo de este tipo, y su mandato los convertiría en no más compatibles con POSIX.

Otras lecturas

JdeBP
fuente
1

Como se señaló en algunas de las otras respuestas, las implementaciones varían. Esto hace que sea difícil estandarizar y preservar la compatibilidad con versiones anteriores de los scripts existentes. Esto es cierto incluso para los sistemas POSIX modernos. Por ejemplo, Linux no tokeniza completamente la línea shebang por espacios. macOS no permite que el intérprete de guiones sea otro guión.

Ver también http://en.wikipedia.org/wiki/Shebang_(Unix)#Portability

jamesdlin
fuente