¿Por qué #! / Usr / bin / env bash es superior a #! / Bin / bash?

212

Lo he visto en varios lugares, incluidas las recomendaciones en este sitio ( ¿Cuál es el Bash shebang preferido? ), Para usar #!/usr/bin/env bashcon preferencia #!/bin/bash. Incluso he visto a una persona emprendedora sugerir que el uso #!/bin/bashera incorrecto y que la funcionalidad bash se perdería al hacerlo.

Dicho todo esto, uso bash en un entorno de prueba estrictamente controlado donde cada unidad en circulación es esencialmente un clon de una sola unidad maestra. Entiendo el argumento de la portabilidad, aunque no es necesariamente aplicable en mi caso. ¿Hay alguna otra razón para preferir #!/usr/bin/env basha las alternativas y, suponiendo que la portabilidad fuera una preocupación, hay alguna razón para usarla que podría romper la funcionalidad?

spugm1r3
fuente
77
No es necesariamente mejor. Vea esta pregunta y mi respuesta en unix.stackexchange.com. (Votaría para cerrar esto como un duplicado, pero no creo que pueda hacerlo en todos los sitios.)
Keith Thompson
2
Además de la respuesta de @ zigg, es envposible que no se encuentre en /usr/bin. Los comentarios de Shebang son una mala idea en mi humilde opinión. Si su intérprete de script predeterminado no maneja los comentarios shebang, es solo un comentario. Sin embargo, si sabe que el intérprete de guiones puede manejar comentarios shebang, y conoce la ruta de acceso a bash, no hay razón para no invocarlo usando su ruta absoluta a menos que la ruta sea demasiado larga (poco probable), o posiblemente pueda portar el guión a un sistema que no tiene bash ubicado en / bin. Por otra parte, las advertencias que mencioné anteriormente se aplican en ese caso ya que implica portabilidad.
1
@KeithThompson, gracias por el enlace. Quizás mi búsqueda de una respuesta antes de publicar la pregunta fue un poco estrecha. Mi conclusión de todo esto: (1) linux / unix / posix / etc ... es gris, y (2) cualquiera que afirme tener absolutamente la respuesta correcta absolutamente tiene la respuesta correcta para su escenario particular.
spugm1r3
3
El comportamiento de muchas cosas en POSIX / Unix está bien definido. Las ubicaciones no siempre son tan claras. Algunas cosas tienen que existir como /etco /bin/sh. bashes un complemento para la mayoría de los sistemas tipo Unix. Es solo Linux donde bashestá garantizado estar /biny muy probablemente también vinculado como /bin/sh. Desde que Linux se convirtió en el Unix moderno de facto para muchas personas, se ha olvidado el hecho de que podrían existir sistemas distintos de Linux. En mi propia respuesta a continuación, asumí Linux porque dijiste bash. Muchas de las cajas BSD con las que he trabajado ni siquiera lo tenían instalado.
Sean Perry
2
@Keith - En el caso de Bash (a diferencia de Python en las otras preguntas) ... OpenBSD no tiene un /bin/bash. Bash no está instalado por defecto. Si lo quieres, tienes que hacerlo pkg install bash. Una vez instalado, se encuentra en /usr/local/bin/bash. No hay nada instalado en /bin/bashOpenBSD. Un shebang de #!/bin/basherror de voluntad y #!/usr/bin/env bashtendrá éxito.
jww

Respuestas:

229

#!/usr/bin/envbúsquedas PATHpara bash, y bashno siempre está en /bin, sobre todo en los sistemas no Linux. Por ejemplo, en mi sistema OpenBSD, está dentro /usr/local/bin, ya que se instaló como un paquete opcional.

Si estás absolutamente seguro bash está /biny siempre lo estará, no hay ningún daño en ponerlo directamente en su shebang, pero lo recomendaría en contra porque los scripts y los programas tienen vidas más allá de lo que inicialmente creemos que tendrán.

zigg
fuente
29
¿Qué pasa con la envubicación? POSIX no lo fuerza.
Julio Guerra
1
@JulioGuerra Al igual que tener un binario /usr/lib/sendmail(o, más recientemente, /usr/sbin/sendmail) disponible para procesar el correo, lo mejor para un sistema similar a Unix es tenerlo /usr/bin/envporque el entorno de envio es una práctica muy común. Es una interfaz estándar de facto.
zigg
8
@zigg Eso es tan UN * X ... <-: Quiero decir, les conviene tener una ubicación estándar env, pero de alguna manera no tener una ubicación estándar (que puede ser un enlace suave) bash. Sin mencionar, por qué entonces hashbang no acepta solo #!bash, y usa el PATH, en lugar de hacer exactamente lo mismo con env. Supongo que no es lo suficientemente confuso para los novatos.
ddekany
1
@JulioGuerra Según Wikipedia, usted es cierto: no está "garantizado". En cambio, parece "más probable". Wikipedia dice This mostly works because the path /usr/bin/env is commonly used for the env utilityaquí en.wikipedia.org/wiki/Shebang_(Unix) - Debemos confiar en envestar allí "probablemente" en todos los sistemas.
Xavi Montero el
2
@XaviMontero: he usado un sistema donde envestaba /bin, no dentro /usr/bin(no estoy seguro de cuál, probablemente SunOS 4). En estos días es muy probable /usr/bin/envque esté disponible, solo por la popularidad del #!/usr/bin/envhack.
Keith Thompson
39

La ubicación estándar de bash es /bin , y sospecho que eso es cierto en todos los sistemas. Sin embargo, ¿qué pasa si no te gusta esa versión de bash? Por ejemplo, quiero usar bash 4.2, pero el bash en mi Mac está en 3.2.5.

Podría intentar reinstalar bash /binpero eso puede ser una mala idea. Si actualizo mi sistema operativo, se sobrescribirá.

Sin embargo, podría instalar bash /usr/local/bin/bashy configurar mi RUTA para:

PATH="/usr/local/bin:/bin:/usr/bin:$HOME/bin"

Ahora, si lo especifico bash, no obtengo el viejo y sucio /bin/bash, sino el más nuevo y brillante /usr/local/bin. ¡Agradable!

Excepto que mis scripts de shell tienen ese !# /bin/bashshebang. Por lo tanto, cuando ejecuto mis scripts de shell, obtengo esa versión antigua y pésima de bash que ni siquiera tiene matrices asociativas.

Utilizando /usr/bin/env bash usará la versión de bash que se encuentra en mi RUTA. Si configuro mi RUTA, para que/usr/local/bin/bash se ejecute, esa es la bash que usarán mis scripts.

Es raro ver esto con bash, pero es mucho más común con Perl y Python:

  • Ciertas versiones de Unix / Linux que se centran en la estabilidad veces están muy rezagadas con el lanzamiento de estos dos lenguajes de secuencias de comandos. No hace mucho tiempo, Perl de RHEL estaba en 5.8.8, ¡una versión de Perl de ocho años! Si alguien quería usar funciones más modernas, tenía que instalar su propia versión.
  • Programas como Perlbrew y Pythonbrew le permiten instalar múltiples versiones de estos idiomas. Dependen de los scripts que manipulan su RUTA para obtener la versión que desea. Codificar el camino significa que no puedo ejecutar mi script en brew .
  • No fue hace tanto tiempo (está bien, fue hace mucho tiempo) que Perl y Python no eran paquetes estándar incluidos en la mayoría de los sistemas Unix. Eso significaba que no sabías dónde estaban instalados estos dos programas. ¿Estaba debajo /bin? /usr/bin? /opt/bin? ¿Quién sabe? Usar #! /usr/bin/env perlsignificaba que no tenía que saberlo.

Y ahora por qué no deberías usar #! /usr/bin/env bash

Cuando el camino está codificado en el shebang, tengo que correr con ese intérprete. Por lo tanto, #! /bin/bashme obliga a usar la versión predeterminada instalada de bash. Dado que las características de bash son muy estables (intente ejecutar una versión 2.x de un script de Python en Python 3.x) es muy poco probable que mi script de BASH en particular no funcione, y dado que este script y otros sistemas probablemente utilicen mi script de bash , el uso de una versión no estándar de bash puede tener efectos no deseados. Es muy probable que quiera asegurarme de que la versión estándar estable de bash se use con mi script de shell. Por lo tanto, probablemente quiero codificar la ruta en mi shebang.

David W.
fuente
55
Esto no es cierto: "La ubicación estándar de bash es / bin" (a menos que pueda citar un documento estándar) es quizás más preciso que la ubicación "habitual" en la mayoría de las distribuciones de Linux y macos, pero no es un estándar en sistemas unix en general (y notablemente no es la ubicación en la mayoría de los * bsds).
tesch1
13

Para invocarlo bashes un poco exagerado. A menos que tengas múltiplesbash archivos binarios como el suyo en ~ / bin, pero eso también significa que su código depende de que $ PATH tenga las cosas correctas.

Sin pythonembargo, es útil para cosas como . Hay scripts de envoltura y entornos que conducen a alternativaspython utilización de binarios .

Pero no se pierde nada al usar la ruta exacta al binario siempre que esté seguro de que es el binario que realmente desea.

Sean Perry
fuente
En el clavo python. He perdido la cuenta de la cantidad de lugares que pythonse pueden encontrar 😀
zigg
2
Acordó que /usr/bin/enves más útil para Python, especialmente si usa virtualenv.
Dennis
10

Hay muchos sistemas que no tienen Bash /bin, FreeBSD y OpenBSD solo por nombrar algunos. Si su script está destinado a ser portátil a muchos Unices diferentes, es posible que desee usar en #!/usr/bin/env bashlugar de#!/bin/bash .

Tenga en cuenta que esto no es cierto para sh; para scripts Bourne conformes I uso exclusivamente #!/bin/sh, ya que creo que casi todos los Unix en la existencia tiene shen /bin.

James Ko
fuente
En Ubuntu 18.04 /bindir, ya veo sh -> dash. Vinculado simbólicamente dash, revelando la naturaleza Debian de Ubuntu. Ejecute estas tres cadenas de comandos para darse cuenta de que todo se reduce a preferencias individuales: which bashluego, which shluego which dash.
noobninja el
0

Preferiría envolver el programa principal en un script como el siguiente para verificar todos los bashdisponibles en el sistema. Es mejor tener más control sobre la versión que usa.

#! /usr/bin/env bash

# This script just chooses the appropriate bash
# installed in system and executes testcode.main

readonly DESIRED_VERSION="5"

declare all_bash_installed_on_this_system
declare bash

if [ "${BASH_VERSINFO}" -ne "${DESIRED_VERSION}" ]
then
    found=0

    all_bash_installed_on_this_system="$(\
        awk -F'/' '$NF == "bash"{print}' "/etc/shells"\
        )"

    for bash in $all_bash_installed_on_this_system
    do
        versinfo="$( $bash -c 'echo ${BASH_VERSINFO}' )"
        [ "${versinfo}" -eq "${DESIRED_VERSION}" ] && { found=1 ; break;}
    done
    if [ "${found}" -ne 1 ]
    then
        echo "${DESIRED_VERSION} not available"
        exit 1
    fi
fi

$bash main_program "$@"
Mihir
fuente
0
 #!/usr/bin/env bash

definitivamente es mejor porque encuentra la ruta ejecutable bash de la variable de entorno del sistema.

Vaya a su shell de Linux y escriba

env

Imprimirá todas sus variables de entorno.

Vaya a su script de shell y escriba

echo $BASH

Imprimirá su ruta bash (de acuerdo con la lista de variables de entorno) que debe usar para construir su ruta shebang correcta en su script.

kta
fuente