Error shebang de FreeBSD

Aug 22 2020

Me gustaría poner shebang #!/bin/sh -eufo pipefailen mi guión. Pero hay varias cosas extrañas:

  1. El script fallaría con ese shebang en FreeBSD pero no cuando se ejecuta en MacOS
  2. en FreeBSd, el mismo shebang funciona cuando se ejecuta directamente desde la línea de comandos (también /bin/sh).
>>> sh -eufo pipefail -c 'echo hi'  # this works
hi

>>> cat <<EOF > script                                                                                      
#!/bin/sh -eufo pipefail
echo hi
EOF

>>> chmod +x ./script 
>>> ./script  # this doesn't work on FreeBSD but works on MacOS
Illegal option -o ./script

>>> cat ./script 
#!/bin/sh -eufo pipefail
echo hi

>>> uname -a
FreeBS 11.3-RELEASE-p7

Respuestas

1 JdeBP Aug 22 2020 at 20:11

MacOS aún conserva el antiguo comportamiento de FreeBSD de antes de 2005. En 2005, hubo un cambio importante en la forma en que el kernel de FreeBSD manejaba #!el inicio de un archivo ejecutable transferido execve(), para alinearlo más con algunos otros kernels del sistema operativo. incluyendo Linux y el kernel de NetBSD.

El comentario en el código fuente del kernel de NetBSD intenta pintar esto como un universal:

* recopilar el argumento de la shell. todo después del nombre de la cáscara
 * se pasa como UN argumento; ese es el correcto (histórico)
 * comportamiento.
- kern/exec-script.c. NetBSD. líneas 189 y siguientes.

En realidad no lo es. Sven Mascheck hizo algunas pruebas hace aproximadamente una década y hay cuatro comportamientos básicos, el AT&T Unix System 5 tiene tanto reclamo de ser un comportamiento "histórico correcto" como el 4.2BSD:

  • Ignore los caracteres (antes de 4.2BSD y AT&T Unix System 5).
  • Pase toda la cadena en un solo argumento (4.2BSD, NetBSD, Linux y FreeBSD desde 2005 en adelante).
  • Divida la cadena por espacios en blanco y páselo como múltiples argumentos (FreeBSD antes de 2005 y MacOS).
  • Divida la cadena por espacios en blanco y pase solo el primer argumento (AT&T Unix System 5 y Solaris)

Solo he incluido los sistemas operativos relevantes para esta respuesta entre paréntesis. M. Mascheck verificó mucho más, al igual que Ahmon Dancy en la discusión del Informe de problemas 16393 de FreeBSD. Consulte la lectura adicional para ver las listas completas.

Lo que trajo las cosas a un punto crítico en FreeBSD en 2005 fue que, irónicamente, FreeBSD no era tan simple como eso. Se había introducido un cambio que tenía la intención de hacer que las cosas escritas en libros populares sobre Perl realmente funcionaran: los argumentos se omitían después de un carácter de comentario. Los libros habían recomendado cosas como:

#! / bin / sh - # - * - perl - * -
- Larry Wall, Tom Christiansen, Jon Orwant (2000). Programación Perl: 3ª edición . O'Reilly Media. ISBN 9780596000271. pág. 488.

PR 16393 en 2000 era una forma de hacer que el kernel manejara scripts de Perl ejecutables, escritos de la manera que Larry Wall había dicho que funcionaría. Sin embargo, rompió otras cosas y no funcionó por completo.

Hubo algunos idas y venidas sobre esto. Finalmente, en 2005, el mecanismo para hacer que la idea de Larry Wall et al. Funcionara se movió fuera del kernel, que se hizo para comportarse de manera compatible con Linux, NetBSD y 4.2BSD (en lugar de Solaris y AT&T Unix System 5) y se hizo la responsabilidad de sh.

El comportamiento desde 2005 ha sido, por lo tanto, que el shell obtiene tres argumentos, el segundo argumento es la cola completa de la #!línea, e invocar su script directamente con execve()es efectivamente lo mismo que invocar:

sh '-eufo pipefail' ./script

Debería ser bastante obvio por qué el shell de Almquist (que es lo que shestá en FreeBSD) piensa que ese ./scriptes el argumento de opción para la -oopción, y que está tratando la pipefailparte como opciones adicionales de una sola letra recopiladas detrás -(que no tiene alrededor para procesar todavía).

Una alternativa también obvia es tener set -o pipefailcomo primer comando en el script, como se señala enhttps://unix.stackexchange.com/a/533418/5132para el shell Bourne Again . Sin embargo, esto solo se agregó al shell de FreeBSD Almquist en 2019 y, por lo tanto, solo está disponible en versiones muy recientes de FreeBSD. (El shell de Debian Almquist aún no se ha agregado, a partir de 2020).

Otras lecturas

schily Aug 23 2020 at 13:53

Aunque su #!uso no es portátil porque utiliza más que lo simple:

#!/bin/sh

o el único argumento comúnmente soportado como en:

#!/bin/sh -oneflag

El problema real es que está utilizando una opción no portátil -o pipefail.

Esta es una opción de ksh93y bashno es compatible con otros shells.

Antecedentes:

  • En MacOS, /bin/shes bashque se ha compilado de una manera específica (por ejemplo, para hacer que las secuencias de escape echofuncionen de forma predeterminada) para que sea compatible con POSIX. Dado que bash es compatible pipefail(ver arriba), esto funciona en MacOS.

  • En FreeBSD, /bin/shes ashque no es compatible pipefail.

Tienes dos posibles caminos por recorrer:

  • No utilice set -o pipefail

  • Espere dos años y vuelva a intentarlo. Lo más probable es que esto funcione, porque decidimos agregar esta opción al próximo estándar POSIX (Edición-8) hace 10 meses, consultehttps://www.austingroupbugs.net/view.php?id=789y dado que el próximo estándar POSIX se publicará en aprox. un año, hay una gran posibilidad de que FreeBSD añadirá soporte para set -o pipefailque ashpronto.