Por que #! / Usr / bin / env bash é superior a #! / Bin / bash?

212

Já vi em vários lugares, incluindo recomendações neste site ( qual é o Bash shebang preferido? ), Para usar #!/usr/bin/env bashpreferencialmente #!/bin/bash. Eu já vi um indivíduo empreendedor sugerir usando#!/bin/bash estava errado e a funcionalidade do bash seria perdida ao fazer isso.

Tudo isso dito, eu uso o bash em um ambiente de teste rigidamente controlado, onde cada unidade em circulação é essencialmente um clone de uma única unidade mestre. Entendo o argumento da portabilidade, embora não seja necessariamente aplicável no meu caso. Existe algum outro motivo para preferir #!/usr/bin/env bashas alternativas e, assumindo que a portabilidade era uma preocupação, existe algum motivo para usá-lo que poderia quebrar a funcionalidade?

spugm1r3
fonte
7
Não é necessariamente melhor. Veja esta pergunta e minha resposta em unix.stackexchange.com. (Eu votaria para fechar esta como uma duplicata, mas eu não acho que você pode fazer isso através de sites.)
Keith Thompson
2
Além da resposta de @ zigg, envpode não estar localizado em /usr/bin. Comentários Shebang são completamente uma má idéia IMHO. Se o seu interpretador de script padrão não manipular comentários shebang, é apenas um comentário. No entanto, se você sabe que o intérprete de script pode lidar com comentários shebang e você conhece o caminho para o bash, não há razão para não invocá-lo usando seu caminho absoluto, a menos que o caminho seja muito longo (improvável) ou você possa portar o script para um sistema que não possui bash localizado em / bin. Por outro lado, as advertências que mencionei anteriormente se aplicam nesse caso, pois envolve portabilidade.
1
@ KeithThompson, obrigado pelo link. Talvez minha busca por uma resposta antes de postar a pergunta tenha sido um pouco restrita. Minha opinião sobre tudo isso: (1) linux / unix / posix / etc ... é cinza e (2) qualquer pessoa que afirme ter absolutamente a resposta certa tem a resposta certa para o seu cenário particular.
Spugm1r3
3
O comportamento de muitas coisas no POSIX / Unix está bem definido. Os locais nem sempre são tão claros. Algo tem que existir como /etcou /bin/sh. bashé um complemento para a maioria dos sistemas similares ao Unix. É apenas o Linux onde bashé garantido estar /bine provavelmente também está vinculado como /bin/sh. Desde que o Linux se tornou o moderno Unix de fato para muitas pessoas, o fato de outros sistemas que não o Linux existirem foi esquecido. Na minha própria resposta abaixo, assumi o Linux porque você disse bash. Muitas das caixas BSD com as quais trabalhei nem sequer a instalaram.
Sean Perry
2
@ Keith - No caso do Bash (em oposição ao Python nas outras perguntas) ... O OpenBSD não possui um /bin/bash. O Bash não está instalado por padrão. Se você quiser, você precisa pkg install bash. Uma vez instalado, ele está localizado em /usr/local/bin/bash. Não há nada instalado no /bin/bashOpenBSD. Um shebang de #!/bin/bashvontade de erro, e #!/usr/bin/env bashterá sucesso.
jww 12/05/19

Respostas:

229

#!/usr/bin/envpesquisas PATHpara bash, e bashnem sempre está em /bin, particularmente em sistemas não-Linux. Por exemplo, no meu sistema OpenBSD, ele está /usr/local/bininstalado, pois foi instalado como um pacote opcional.

Se você tem absoluta certeza de que bashestá dentro /bine sempre estará, não há mal em colocá-lo diretamente em sua mente - mas eu recomendaria isso porque todos os scripts e programas têm uma vida além do que inicialmente acreditamos que eles terão.

zigg
fonte
29
e a envlocalização? O POSIX não o força.
Julio Guerra
1
@JulioGuerra Assim como ter um binário /usr/lib/sendmail(ou, mais recentemente /usr/sbin/sendmail) disponível para processar e-mails, é do interesse de um sistema semelhante ao Unix, /usr/bin/envporque o ambiente é uma prática comum. É uma interface padrão de fato.
zigg
8
@zigg Isso é tão UN * X ... <-: quero dizer, é do interesse deles ter uma localização padrão para env, mas de alguma forma não ter uma localização padrão (que pode ser apenas um link suave) bash. Sem mencionar, por que então o hashbang não aceita apenas #!bashe usa o PATH, em vez de fazer exatamente o mesmo com env. Não é confuso o suficiente para novatos, eu acho.
ddekany
1
@JulioGuerra De acordo com a wikipedia, você é verdadeiro: não é "garantido". Em vez disso, parece "mais provável". A Wikipedia diz This mostly works because the path /usr/bin/env is commonly used for the env utilityaqui en.wikipedia.org/wiki/Shebang_(Unix) - Devemos confiar em envestar lá "provavelmente" em todos os sistemas.
Xavi Montero
2
@XaviMontero: Eu usei um sistema em que envestava /bin, não no /usr/bin(não tenho certeza de qual deles, provavelmente SunOS 4). Hoje em dia é muito provável /usr/bin/envque esteja disponível, apenas por causa da popularidade do #!/usr/bin/envhack.
Keith Thompson
39

A localização padrão do bash é /bine suspeito que isso seja verdade em todos os sistemas. No entanto, e se você não gostar dessa versão do bash? Por exemplo, eu quero usar o bash 4.2, mas o bash no meu Mac está em 3.2.5.

Eu poderia tentar reinstalar o bash, /binmas isso pode ser uma má ideia. Se eu atualizar meu sistema operacional, ele será substituído.

No entanto, eu poderia instalar o bash in /usr/local/bin/bashe configurar meu PATH para:

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

Agora, se eu especificar bash, não entendo o antigo e bruto /bin/bash, mas o mais novo e mais brilhante /usr/local/bin. Agradável!

Exceto que meus scripts de shell têm essa !# /bin/bashaparência. Portanto, quando executo meus scripts de shell, obtenho a versão antiga e ruim do bash que nem sequer possui matrizes associativas.

Usar /usr/bin/env bashusará a versão do bash encontrada no meu PATH. Se eu configurar meu PATH, para que/usr/local/bin/bash seja executado, essa será a base que meus scripts usarão.

É raro ver isso com o bash, mas é muito mais comum no Perl e Python:

  • Algumas versões do Unix / Linux que se concentram na estabilidade às vezes estão muito atrasadas com o lançamento dessas duas linguagens de script. Há pouco tempo, o Perl do RHEL estava em 5.8.8 - uma versão de oito anos do Perl! Se alguém quisesse usar recursos mais modernos, você teria que instalar sua própria versão.
  • Programas como Perlbrew e Pythonbrew permitem instalar várias versões desses idiomas. Eles dependem de scripts que manipulam seu PATH para obter a versão desejada. A codificação do caminho significa que não consigo executar meu script em preparação .
  • Não faz muito tempo (tudo bem, faz muito tempo) que Perl e Python não eram pacotes padrão incluídos na maioria dos sistemas Unix. Isso significava que você não sabia onde esses dois programas foram instalados. Estava embaixo /bin? /usr/bin? /opt/bin? Quem sabe? Usar #! /usr/bin/env perlsignificava que eu não precisava saber.

E agora, por que você não deve usar #! /usr/bin/env bash

Quando o caminho é codificado no shebang, eu tenho que correr com esse intérprete. Assim, #! /bin/bashme obriga a usar a versão padrão instalada do bash. Como os recursos do bash são muito estáveis ​​(tente executar uma versão 2.x de um script Python no Python 3.x), é muito improvável que meu script BASH em particular não funcione, e como meu script bash provavelmente é usado por esse sistema e outros sistemas , o uso de uma versão não padrão do bash pode ter efeitos indesejados. É muito provável que eu queira ter certeza de que a versão padrão estável do bash seja usada com meu script de shell. Assim, eu provavelmente quero codificar o caminho no meu shebang.

David W.
fonte
5
Isso não é verdade: "O local padrão do bash é / bin" (a menos que você possa citar um documento padrão) talvez seja mais preciso que seja o local "usual" na maioria das distribuições e macos do Linux, mas não é um padrão em sistemas unix em geral (e notavelmente não é o local na maioria dos * bsds).
tesch1
13

Invocar bashé um pouco exagerado. A menos que você tenha vários bashbinários como o seu em ~ / bin, mas isso também significa que seu código depende de $ PATH ter as coisas certas.

É útil para coisas como isso python. Existem scripts e ambientes de wrapper que levam a pythonbinários alternativos sendo usados.

Mas nada se perde usando o caminho exato para o binário, desde que você tenha certeza de que é o binário que realmente deseja.

Sean Perry
fonte
Spot on for python. Eu perdi a conta do número de lugares que pythonpodem ser encontrados 😀
zigg
2
Concordou que /usr/bin/envé mais útil para Python, especialmente se você usar virtualenv.
Dennis
10

Existem muitos sistemas que não possuem o Bash /bin, FreeBSD e OpenBSD apenas para citar alguns. Se o seu script for portátil para muitos Unices diferentes, convém usá-lo em #!/usr/bin/env bashvez de#!/bin/bash .

Observe que isso não é verdadeiro para sh; para scripts Bourne compatíveis I exclusivamente usar #!/bin/sh, desde que eu acho que praticamente todos os Unix na existência tem shem /bin.

James Ko
fonte
No Ubuntu 18.04 /bindir, eu vejo sh -> dash. Simbolicamente vinculado a dash, revelando a natureza Debian do Ubuntu. Executar estas três cadeias de comando para perceber que tudo se resume a preferência individual: which bashem seguida, which shem seguida which dash.
noobninja
0

Eu preferiria agrupar o programa principal em um script como abaixo para verificar todos os bashdisponíveis no sistema. Melhor ter mais controle sobre a versão usada.

#! /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
fonte
0
 #!/usr/bin/env bash

é definitivamente melhor porque encontra o caminho executável do bash a partir da variável de ambiente do sistema.

Vá para o seu shell Linux e digite

env

Ele imprimirá todas as suas variáveis ​​de ambiente.

Vá para o seu shell script e digite

echo $BASH

Ele imprimirá o caminho do bash (de acordo com a lista de variáveis ​​de ambiente) que você deve usar para criar o caminho correto do shebang em seu script.

kta
fonte