Qual é a diferença entre "#! / Usr / bin / env bash" e "#! / Usr / bin / bash"?

372

No cabeçalho de um script Bash, qual é a diferença entre essas duas instruções:

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

Quando consultei a env página de manual , recebo esta definição:

 env - run a program in a modified environment

O que isso significa?

tarrsalah
fonte
7
Veja esta pergunta e minha resposta .
Keith Thompson
6
Quem pode me dizer por que essa pergunta foi encerrada, "relacionado à programação ou desenvolvimento de software" não é?
tarrsalah
11
Concordo que não é fora de tópico, mas provavelmente é uma duplicata de várias outras perguntas como esta .
Keith Thompson
16
Esta pergunta não deveria ter sido marcada como off-topic. Ele precisa apenas de 5 pessoas com uma pontuação acima de 3000 para marcá-lo como "no tópico" e pode ser reaberto. É uma pergunta - especificamente sobre programação.
Danijel-James W
3
Estou chocado. Chocado ao descobrir que a documentação do Linux está repleta de tautologias. xkcd.com/703 git-man-page-generator.lokaltog.net
allyourcode

Respostas:

323

Executando um meio de comando /usr/bin/envtem a vantagem de olhar para o que a versão padrão do programa está na sua atual env ironment.

Dessa forma, você não precisa procurá-lo em um local específico no sistema, pois esses caminhos podem estar em locais diferentes em sistemas diferentes. Enquanto estiver no seu caminho, ele o encontrará.

Uma desvantagem é que você não conseguirá passar mais de um argumento (por exemplo, você não conseguirá escrever /usr/bin/env awk -f) se desejar dar suporte ao Linux, pois o POSIX é vago sobre como a linha deve ser interpretada e o Linux interpreta tudo após o primeiro espaço para denotar um único argumento. Você pode usar /usr/bin/env -Sem algumas versões envpara contornar isso, mas o script se tornará ainda menos portátil e quebrará em sistemas bastante recentes (por exemplo, até o Ubuntu 16.04, se não depois).

Outra desvantagem é que, como você não está chamando um executável explícito, há o potencial de erros e de problemas de segurança nos sistemas multiusuário (se alguém conseguisse chamar o executável bashno seu caminho, por exemplo).

#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash     #gives you explicit control on a given system of what executable is called

Em algumas situações, o primeiro pode ser preferido (como executar scripts python com várias versões do python, sem precisar refazer a linha executável). Mas em situações em que a segurança é o foco, o último seria preferido, pois limita as possibilidades de injeção de código.

Alec Bennett
fonte
22
Outra desvantagem é que você não pode passar um argumento adicional para o intérprete.
Keith Thompson
11
@KeithThompson: Informações incorretas. Você pode passar opções para o intérprete subjacente usando / usr / bin / env!
Gaurav Agarwal
4
@GauravAgarwal: Não está no meu sistema. Um script contendo apenas esta única linha: #!/usr/bin/env echo Helloreclama: /usr/bin/env: echo Hello: No such file or directory. Aparentemente, trata echo Hellocomo um único argumento para /usr/bin/env.
Keith Thompson
11
@ AndréLaszlo: O envcomando certamente permite que argumentos sejam passados ​​para o comando. A questão é a semântica da #!linha, e isso depende do kernel. Os kernels recentes do Linux permitem coisas assim #!/usr/bin/env command args, mas os antigos do Linux e outros sistemas não.
Keith Thompson
11
Por que existem backticks no bloco de código? Eles não deveriam ser removidos?
Benjamin W.
64

Usar #!/usr/bin/env NAMEfaz com que o shell procure a primeira correspondência de NAME na variável de ambiente $ PATH. Pode ser útil se você não estiver ciente do caminho absoluto ou não quiser procurá-lo.

Dolphiniac
fonte
9
Pelo menos você tem que saber onde é env :).
ᐅ devrimbaris
11
Excelente resposta. Explica sucintamente o que o shebang env faz, ao invés de dizer "escolhe o programa com base na configuração do sistema"
De Novo
13

Em vez de definir explicitamente o caminho para o intérprete como em /usr/bin/bash/, usando o comando env, o intérprete é procurado e iniciado a partir de onde quer que seja encontrado. Isso tem vantagens e desvantagens

safay
fonte
Na maioria dos sistemas, eles serão funcionalmente iguais, mas isso depende da localização dos executáveis ​​bash e env. Não tenho certeza de como isso afetará as variáveis ​​de ambiente.
safay
2
"É possível especificar o intérprete sem usar env, fornecendo o caminho completo para o intérprete. Um problema é que, em diferentes sistemas de computadores, o caminho exato pode ser diferente. Ao usar env, o intérprete é procurado e localizado em a hora em que o script é executado. Isso torna o script mais portátil, mas também aumenta o risco de o intérprete errado ser selecionado porque ele procura uma correspondência em todos os diretórios no caminho de pesquisa executável. Ele também sofre do mesmo problema em que o O caminho para o binário de ambiente também pode ser diferente em cada máquina. "- Wikipedia
Mike Clark
10

Se os scripts do shell começarem #!/bin/bash, eles sempre serão executados com bashfrom /bin. Se eles no entanto começar com #!/usr/bin/env bash, eles vão procurar bashem $PATHe, em seguida, começar com o primeiro que possam encontrar.

Por que isso seria útil? Suponha que você queira executar bashscripts, que exijam o bash 4.x ou mais recente, mas seu sistema só possui bash3.x instalado e atualmente sua distribuição não oferece uma versão mais recente ou você não é administrador e não pode alterar o que está instalado nesse sistema .

Obviamente, você pode baixar o código-fonte do bash e criar seu próprio bash do zero, colocando-o como, ~/binpor exemplo. E você também pode modificar sua $PATHvariável em seu .bash_profilearquivo para incluir ~/bincomo a primeira entrada ( PATH=$HOME/bin:$PATHpois ~não será expandida $PATH). Se você ligar agora bash, o shell primeiro procurará por ele $PATHem ordem, para que comece com ~/binonde encontrará o seu bash. O mesmo acontece se os scripts procurarem bashusar #!/usr/bin/env bash, portanto, esses scripts agora estariam trabalhando no seu sistema usando sua bashcompilação personalizada .

Uma desvantagem é que isso pode levar a um comportamento inesperado, por exemplo, o mesmo script na mesma máquina pode ser executado com diferentes intérpretes para diferentes ambientes ou usuários com diferentes caminhos de pesquisa, causando todo tipo de dor de cabeça.

A maior desvantagem envé que alguns sistemas permitem apenas um argumento, então você não pode fazer isso#!/usr/bin/env <interpreter> <arg> , pois os sistemas o verão <interpreter> <arg>como um argumento (eles o tratarão como se a expressão estivesse citada) e, assim env, procurarão um intérprete chamado <interpreter> <arg>. Observe que isso não é um problema do envpróprio comando, que sempre permite a passagem de vários parâmetros, mas com o analisador shebang do sistema que analisa essa linha antes mesmo de chamar env. Enquanto isso, isso foi corrigido na maioria dos sistemas, mas se seu script quiser ser ultra portátil, você não pode confiar que isso foi corrigido no sistema em que você estará executando.

Pode até ter implicações de segurança, por exemplo, se sudonão foi configurado para limpar o ambiente ou $PATHfoi excluído da limpeza. Deixe-me demonstrar isso:

Geralmente /biné um local bem protegido, só rooté capaz de mudar qualquer coisa lá. Seu diretório inicial não é, no entanto, qualquer programa que você executa, é capaz de fazer alterações nele. Isso significa que o código malicioso pode colocar um falso bashem algum diretório oculto, modificá-lo .bash_profilepara incluir esse diretório no seu $PATH, para que todos os scripts usados #!/usr/bin/env bashacabem sendo executados com esse falsobash . Se sudomantém $PATH, você está com um grande problema.

Por exemplo, considere que uma ferramenta cria um arquivo ~/.evil/bashcom o seguinte conteúdo:

#!/bin/bash

if [ $EUID -eq 0 ]; then
  echo "All your base are belong to us..."
  # We are root - do whatever you want to do
fi

/bin/bash "$@"

Vamos fazer um script simples sample.sh:

#!/usr/bin/env bash

echo "Hello World"

Prova de conceito (em um sistema em que sudo fica $PATH):

$ ./sample.sh
Hello World

$ sudo ./sample.sh
Hello World

$ export PATH="$HOME/.evil:$PATH"

$ ./sample.sh
Hello World

$ sudo ./sample.sh
All your base are belong to us...
Hello World

Geralmente, as conchas clássicas devem estar localizadas /bine, se você não quiser colocá-las lá por qualquer motivo, não é um problema colocar um link simbólico /binque aponte para seus locais reais (ou talvez /binele próprio seja um link simbólico), então Eu sempre iria com #!/bin/she#!/bin/bash . Há muita coisa que poderia quebrar se não funcionasse mais. Não é que o POSIX exija essa posição (o POSIX não padroniza os nomes dos caminhos e, portanto, nem sequer padroniza o recurso shebang), mas eles são tão comuns que, mesmo que um sistema não ofereça um /bin/sh, provavelmente ainda entenderá #!/bin/she saiba o que fazer com ele, e pode ser apenas para compatibilidade com o código existente.

Mas para intérpretes opcionais mais modernos, não padrão e opcionais, como Perl, PHP, Python ou Ruby, isso não é realmente especificado em nenhum lugar onde eles devem estar localizados. Eles podem estar em /usr/binmas eles podem também estar em /usr/local/binou num ramo hierarquia completamente diferente ( /opt/..., /Applications/..., etc.). É por isso que eles costumam usar a #!/usr/bin/env xxxsintaxe shebang.

Mecki
fonte
4

Acho útil, porque quando eu não sabia sobre o env, antes de começar a escrever o script, estava fazendo o seguinte:

type nodejs > scriptname.js #or any other environment

e então eu estava modificando essa linha no arquivo para shebang.
Eu estava fazendo isso, porque nem sempre me lembro onde está o nodejs no meu computador - / usr / bin / ou / bin /, portanto, para mim, envé muito útil. Talvez haja detalhes com isso, mas esta é a minha razão

sudo97
fonte