Percebo que alguns scripts que adquiri de outras pessoas têm o shebang, #!/path/to/NAME
enquanto outros (usando a mesma ferramenta, NAME) têm o shebang #!/usr/bin/env NAME
.
Ambos parecem funcionar corretamente. Nos tutoriais (em Python, por exemplo), parece haver uma sugestão de que o último shebang é melhor. Mas não entendo muito bem por que isso acontece.
Sei que, para usar o último shebang, NAME deve estar no PATH, enquanto o primeiro shebang não possui essa restrição.
Além disso, parece (para mim) que o primeiro seria o melhor shebang, pois especifica exatamente onde NAME está localizado. Portanto, neste caso, se houver várias versões de NAME (por exemplo, / usr / bin / NAME, / usr / local / bin / NAME), o primeiro caso especifica qual usar.
Minha pergunta é por que o primeiro shebang é preferido ao segundo?
fonte
Respostas:
Não é necessariamente melhor.
A vantagem
#!/usr/bin/env python
é que ele usará opython
executável que aparecer primeiro no usuário$PATH
.A desvantagem de
#!/usr/bin/env python
é que ele vai usar qualquerpython
executável aparece em primeiro lugar o usuário do$PATH
.Isso significa que o script pode se comportar de maneira diferente, dependendo de quem o executa. Para um usuário, ele pode usar o
/usr/bin/python
que foi instalado com o sistema operacional. Por outro lado, pode usar um experimental/home/phred/bin/python
que não funciona corretamente.E se
python
é instalado apenas em/usr/local/bin
, um usuário que não tem/usr/local/bin
em$PATH
não vai mesmo ser capaz de executar o script. (Isso provavelmente não é muito provável nos sistemas modernos, mas pode acontecer facilmente para um intérprete mais obscuro.)Ao especificar,
#!/usr/bin/python
você especifica exatamente qual intérprete será usado para executar o script em um sistema específico .Outro problema em potencial é que o
#!/usr/bin/env
truque não permite que você passe argumentos para o intérprete (exceto o nome do script, que é passado implicitamente). Isso geralmente não é um problema, mas pode ser. Muitos scripts Perl são escritos com#!/usr/bin/perl -w
, masuse warnings;
é a substituição recomendada atualmente. Os scripts#!/bin/csh -f
csh devem usar - mas os scripts csh não são recomendados em primeiro lugar. Mas poderia haver outros exemplos.Eu tenho vários scripts Perl em um sistema de controle de origem pessoal que instalo quando configuro uma conta em um novo sistema. Eu uso um script de instalação que modifica a
#!
linha de cada script à medida que o instala no meu$HOME/bin
. (Eu não precisei usar nada além de#!/usr/bin/perl
ultimamente; ele remonta aos tempos em que o Perl geralmente não era instalado por padrão.)Um ponto secundário: o
#!/usr/bin/env
truque é indiscutivelmente um abuso doenv
comando, que foi originalmente planejado (como o nome indica) para invocar um comando com um ambiente alterado. Além disso, alguns sistemas mais antigos (incluindo SunOS 4, se bem me lembro) não tinha oenv
comando/usr/bin
. É provável que nenhum destes seja uma preocupação significativa.env
funciona dessa maneira, muitos scripts usam esse#!/usr/bin/env
truque, e os provedores de SO provavelmente não farão nada para quebrá-lo. Ele pode ser um problema se você quiser que seu script para ser executado em um sistema muito antigo, mas, em seguida, é provável que você precisa modificá-lo de qualquer maneira.Outra questão possível (graças a Sopalajo de Arrierez por apontar nos comentários) é que os trabalhos cron são executados em um ambiente restrito. Em particular,
$PATH
normalmente é algo parecido/usr/bin:/bin
. Portanto, se o diretório que contém o intérprete não estiver em um desses diretórios, mesmo que esteja no seu padrão$PATH
em um shell de usuário, o/usr/bin/env
truque não funcionará. Você pode especificar o caminho exato ou adicionar uma linha ao seu crontab para definir$PATH
(man 5 crontab
para detalhes).fonte
use v5.12;
serve a alguns desses propósitos. E#!/usr/bin/env perl5.12
falhará se o sistema tiver Perl 5.14, mas não 5.12. Para Python 2 vs. 3,#!/usr/bin/python2
e#!/usr/bin/python3
provavelmente funcionará./usr/local/bin
. Por acaso sei que ele funciona corretamente/usr/bin/perl
. Não tenho idéia se funciona com qualquerperl
executável que algum usuário aleatório possua em seu computador$PATH
. Talvez alguém esteja experimentando alguma versão antiga do Perl; porque eu especifiquei#!/usr/bin/perl
, meu script (que o usuário nem sequer conhece ou se importa é um script Perl) não para de funcionar./usr/bin/perl
, descobrirei rapidamente, e é responsabilidade do proprietário / administrador do sistema mantê-lo atualizado. Se você deseja executar meu script com seu próprio perl, sinta-se à vontade para pegar e modificar uma cópia ou invocá-la viaperl foo
. (E você pode considerar a possibilidade de que as 55 pessoas que aprovaram esta resposta também saibam uma coisa ou duas. Certamente é possível que você esteja certo e que tudo esteja errado, mas não é assim que eu aposto.)Porque / usr / bin / env pode interpretar o seu
$PATH
, o que torna os scripts mais portáteis.Só executará seu script se o python estiver instalado em / usr / local / bin.
Irá interpretar o seu
$PATH
e encontrar python em qualquer diretório no seu$PATH
.Portanto, seu script é mais portátil e funcionará sem modificações nos sistemas em que o python está instalado como
/usr/bin/python
, ou/usr/local/bin/python
, ou mesmo em diretórios personalizados (aos quais foram adicionados$PATH
), como/opt/local/bin/python
.A portabilidade é a única razão pela qual o uso
env
é preferível aos caminhos codificados.fonte
python
executáveis são particularmente comuns à medida que ovirtualenv
uso aumenta.#! python
, por que isso não é usado?#! python
não é usado porque você precisa estar no mesmo diretório que o binário python, pois a palavra de barrapython
é interpretada como o caminho completo para o arquivo. Se você não possui um binário python no diretório atual, receberá um erro comobash: ./script.py: python: bad interpreter: No such file or directory
. É o mesmo que se você usou#! /not/a/real/path/python
A especificação do caminho absoluto é mais precisa em um determinado sistema. A desvantagem é que é muito preciso. Suponha que você perceba que a instalação do sistema Perl é muito antiga para seus scripts e que você deseja usar os seus próprios: em seguida, você deve editar os scripts e alterar
#!/usr/bin/perl
para#!/home/myname/bin/perl
. Pior, se você tiver o Perl em/usr/bin
algumas máquinas,/usr/local/bin
em outras e/home/myname/bin/perl
em outras, precisará manter três cópias separadas dos scripts e executar a apropriada em cada máquina.#!/usr/bin/env
quebra sePATH
for ruim, mas quase tudo também. Tentar operar com problemas ruinsPATH
raramente é útil e indica que você sabe muito pouco sobre o sistema em que o script está sendo executado, portanto, você não pode confiar em nenhum caminho absoluto.Existem dois programas cuja localização você pode confiar em quase todas as variantes unix:
/bin/sh
e/usr/bin/env
. Algumas variantes obscuras e quase todas aposentadas do Unix tinham/bin/env
sem ter/usr/bin/env
, mas é improvável que você as encontre. Os sistemas modernos têm/usr/bin/env
precisamente por causa de seu amplo uso em shebangs./usr/bin/env
é algo em que você pode contar.Além disso
/bin/sh
, o único momento em que você deve usar um caminho absoluto em um shebang é quando seu script não deve ser portátil, para que você possa contar com um local conhecido para o intérprete. Por exemplo, um script bash que só funciona no Linux pode ser usado com segurança#!/bin/bash
. Um script que deve ser usado apenas internamente pode depender das convenções de localização do intérprete interno.#!/usr/bin/env
tem desvantagens. É mais flexível do que especificar um caminho absoluto, mas ainda exige saber o nome do intérprete. Ocasionalmente, você pode querer executar um intérprete que não esteja no$PATH
, por exemplo, em um local relativo ao script. Nesses casos, muitas vezes você pode criar um script poliglota que possa ser interpretado pelo shell padrão e pelo intérprete desejado. Por exemplo, para tornar um script Python 2 portátil, tanto para sistemas ondepython
é Python 3 epython2
Python 2 quanto para sistemas ondepython
é Python 2 epython2
não existe:fonte
Especificamente para perl, usar
#!/usr/bin/env
é uma má ideia por dois motivos.Primeiro, não é portátil. Em algumas plataformas obscuras, o env não está em / usr / bin. Segundo, como Keith Thompson observou, isso pode causar problemas ao passar argumentos na linha shebang. A solução portátil máxima é esta:
Para obter detalhes sobre como ele funciona, consulte 'perldoc perlrun' e o que ele diz sobre o argumento -x.
fonte
A razão pela qual há uma distinção entre os dois é por causa de como os scripts são executados.
O uso
/usr/bin/env
(que, como mencionado em outras respostas, não está presente em/usr/bin
alguns sistemas operacionais) é necessário porque você não pode simplesmente colocar um nome de executável após o#!
- deve ser um caminho absoluto. Isso ocorre porque o#!
mecanismo funciona em um nível inferior ao do shell. Faz parte do carregador binário do kernel. Isso pode ser testado. Coloque isso em um arquivo e marque-o como executável:Você encontrará que imprime um erro como este ao tentar executá-lo:
Se um arquivo é marcado como executável e começa com a
#!
, o kernel (que não conhece$PATH
ou o diretório atual: estes são conceitos de território do usuário) procurará um arquivo usando um caminho absoluto. Como o uso de um caminho absoluto é problemático (como mencionado em outras respostas), alguém criou um truque: você pode executar/usr/bin/env
(que quase sempre está nesse local) para executar algo usando$PATH
.fonte
Há mais dois problemas com o uso
#!/usr/bin/env
Não resolve o problema de especificar o caminho completo para o intérprete, apenas o move para
env
.env
não é mais garantido estar dentro do/usr/bin/env
quebash
é garantido estar dentro/bin/bash
ou python dentro/usr/bin/python
.env
sobrescreve ARGV [0] pelo nome do intérprete (por exemplo, bash ou python).Isso impede que o nome do seu script apareça, por exemplo, na
ps
saída (ou altera como / onde aparece) e torna impossível encontrá-lo com, por exemplo,ps -C scriptname.sh
[atualização 04/06/2016]
E um terceiro problema:
Alterar seu PATH é mais trabalhoso do que apenas editar a primeira linha de um script, especialmente quando o script de tais edições é trivial. por exemplo:
printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py
Anexar ou prender um diretório a $ PATH é bastante fácil (embora você ainda precise editar um arquivo para torná-lo permanente - o seu
~/.profile
ou o que quer que seja - e está longe de ser fácil criar um script) , não na primeira linha).Alterar a ordem dos diretórios PATH é significativamente mais difícil .... e muito mais difícil do que apenas editar a
#!
linha.E você ainda tem todos os outros problemas que o uso
#!/usr/bin/env
oferece.@jlliagre sugere em um comentário que
#!/usr/bin/env
é útil para testar seu script com várias versões de intérprete, "alterando apenas a ordem PATH / PATH"Se você precisar fazer isso, é muito mais fácil ter várias
#!
linhas na parte superior do seu script (elas são apenas comentários em qualquer lugar, exceto a primeira linha) e recortar / copiar e colar o que você deseja usar agora para a primeira linha.Em
vi
, isso seria tão simples quanto mover o cursor para a#!
linha desejada e digitardd1GP
ouY1GP
. Mesmo com um editor tão trivial quantonano
, levaria alguns segundos para usar o mouse para copiar e colar.No geral, as vantagens do uso
#!/usr/bin/env
são mínimas, na melhor das hipóteses, e certamente nem chegam perto de superar as desvantagens. Até a vantagem da "conveniência" é ilusória.Na IMO, é uma idéia tola promovida por um tipo específico de programador que pensa que os sistemas operacionais não são algo com o qual trabalhar, eles são um problema a ser contornado (ou ignorado, na melhor das hipóteses).
PS: aqui está um script simples para alterar o intérprete de vários arquivos ao mesmo tempo.
change-shebang.sh
:Execute-o como, por exemplo,
change-shebang.sh python2.7 *.py
ouchange-shebang.sh $HOME/bin/my-experimental-ruby *.rb
fonte
csh
o incentivo ao script está promovendo uma solução ruim: meio que funciona, mas existem alternativas muito melhores.#!
linha./usr/bin/env
e eu preciso me livrar dela localmente, ou se não o usarem e eu preciso adicioná-lo. Os dois casos podem ocorrer e, portanto, os scripts fornecidos nesta resposta são uma ferramenta potencialmente útil para se ter na caixa de ferramentas.Adicionando outro exemplo aqui:
O uso
env
também é útil quando você deseja compartilhar scripts entre váriosrvm
ambientes, por exemplo.Executando isso na linha cmd, mostra qual versão ruby será usada quando
#!/usr/bin/env ruby
usada dentro de um script:env ruby --version
Portanto, quando você usa
env
, pode usar diferentes versões do ruby através do rvm, sem alterar seus scripts.fonte
Se você está escrevendo exclusivamente para si ou para o seu trabalho, e o local do intérprete para o qual está ligando está sempre no mesmo lugar, use o caminho direto. Em todos os outros casos, use
#!/usr/bin/env
.Eis o porquê: na sua situação, o
python
intérprete estava no mesmo local, independentemente da sintaxe usada, mas para muitas pessoas ele poderia ter sido instalado em um local diferente. Embora a maioria dos principais intérpretes de programação esteja localizada em/usr/bin/
muitos softwares mais recentes, ela está por padrão/usr/local/bin/
.Eu diria mesmo que sempre use,
#!/usr/bin/env
porque se você tem várias versões do mesmo intérprete instaladas e não sabe o que seu shell usará como padrão, provavelmente deverá consertar isso.fonte
Por motivos de portabilidade e compatibilidade, é melhor usar
ao invés de
Existem várias possibilidades, nas quais um binário pode estar localizado em um sistema Linux / Unix. Verifique a página de manual hier (7) para obter uma descrição detalhada da hierarquia do sistema de arquivos.
O FreeBSD, por exemplo, instala todo o software, que não faz parte do sistema base, em / usr / local / . Como o bash não faz parte do sistema base, o binário do bash é instalado em / usr / local / bin / bash .
Quando você quer um script portátil bash / csh / perl / qualquer que seja, executado na maioria das distribuições Linux e FreeBSD, você deve usar #! / Usr / bin / env .
Observe também que a maioria das instalações Linux também (hard) vinculou o binário env a / bin / env ou softlinked / usr / bin em / bin, o que não deve ser usado no shebang. Portanto, não use #! / Bin / env .
fonte