Por que as pessoas escrevem o #! / Usr / bin / env python shebang na primeira linha de um script Python?

1047

Parece-me que os arquivos executam o mesmo sem essa linha.

john garcias
fonte
1
A resposta abaixo afirma que é apenas uma linha de comentário. Nem sempre é esse o caso. Eu tenho um "Olá, mundo!" Script CGI (.py) que será executado e exibirá apenas a página da Web #!/usr/bin/env pythonna parte superior.
precisa
Eles podem correr, mas não no ambiente destina
Nicholas Hamilton
18
Eu visitei este post tantas vezes em 7 anos porque às vezes esqueço o env hashbang. Copie o macarrão :)
BugHunterUK

Respostas:

1083

Se você tiver várias versões do Python instaladas, /usr/bin/envgarantirá que o intérprete usado seja o primeiro do seu ambiente $PATH. A alternativa seria codificar algo como #!/usr/bin/python; tudo bem, mas menos flexível.

No Unix, um arquivo executável que deve ser interpretado pode indicar qual intérprete deve ser usado com um #!no início da primeira linha, seguido pelo intérprete (e quaisquer sinalizadores que ele possa precisar).

Se você está falando de outras plataformas, é claro, essa regra não se aplica (mas essa "linha shebang" não faz mal) e ajudará se você copiar esse script para uma plataforma com base em Unix, como Linux, Mac etc).

Alex Martelli
fonte
267
Apenas para adicionar: isso se aplica quando você o executa no Unix, tornando-o executável ( chmod +x myscript.py) e depois executando-o diretamente ./myscript.py:, ao invés de apenas python myscript.py.
Craig McQueen
28
O uso envfornece flexibilidade máxima, pois o usuário pode selecionar o intérprete a ser alterado, alterando o PATH. Muitas vezes, essa flexibilidade não é necessária e o lado negativo é que o Linux, por exemplo, não pode usar o nome do script para o nome do processo pse reverte para "python". Ao empacotar aplicativos python para distribuições, por exemplo, aconselho a não usar env.
pixelbeat
9
pyO iniciador pode usar a linha shebang no Windows. Está incluído no Python 3.3 ou pode ser instalado independentemente .
JFS
6
Uma palavra importante de aviso, o valor de retorno do env expira eventualmente. O que provavelmente não afetará você se você estiver executando processos de curta duração. No entanto, tive processos morrendo com a mensagem /usr/bin/env: Key has expireddepois de muitas horas.
Malaverdiere
4
@malaverdiere você pode vincular a algum recurso que explique esse comportamento de expiração? Não consigo encontrá-los.
Michael
266

Isso é chamado de linha shebang . Como a entrada da Wikipedia explica :

Na computação, um shebang (também chamado de hashbang, hashpling, pound bang ou crunchbang) refere-se aos caracteres "#!" quando eles são os dois primeiros caracteres em uma diretiva de intérprete como a primeira linha de um arquivo de texto. Em um sistema operacional semelhante ao Unix, o carregador de programa toma a presença desses dois caracteres como uma indicação de que o arquivo é um script e tenta executá-lo usando o interpretador especificado pelo restante da primeira linha do arquivo.

Veja também a entrada de perguntas frequentes do Unix .

Mesmo no Windows, onde a linha shebang não determina o intérprete a ser executado, você pode passar opções para o intérprete especificando-as na linha shebang. Acho útil manter uma linha shebang genérica em scripts únicos (como os que escrevo ao responder perguntas no SO), para que eu possa testá-los rapidamente no Windows e no ArchLinux .

O utilitário env permite chamar um comando no caminho:

O primeiro argumento restante especifica o nome do programa a ser chamado; é pesquisado de acordo com a PATHvariável de ambiente. Quaisquer argumentos restantes são passados ​​como argumentos para esse programa.

Sinan Ünür
fonte
30
Fácil de encontrar com o Google - se você souber as palavras-chave (a "linha shebang" é essencial).
Arafangion
14
Na verdade, essa explicação é mais clara do que outras referências que verifiquei com o Google. É sempre melhor obter 1 parágrafo explicando a questão, em vez de ler um manual inteiro abordando todos os usos em potencial.
Sam Goldberg
1
@Arafangion, você provavelmente encontrará esta pergunta útil. TL; DR: symbolhound.com
ulidtko
@ulidtko: Motor de busca interessante, considere escrever uma resposta para que a pergunta de john garcias tenha uma resposta melhor.
Arafangion
1
"Mesmo no Windows, onde a linha shebang não determina o intérprete a ser executado, você pode passar opções para o intérprete especificando-as na linha shebang". Isso é simplesmente falso; se isso acontecer, é porque o próprio intérprete está processando a linha shebang. Se o intérprete não tiver um reconhecimento especial para as linhas de expressão, isso não acontece. O Windows não fazer nada com linhas shebang "O que você pode estar descrevendo neste caso é o lançador de python:. Python.org/dev/peps/pep-0397 .
Kaz
154

Expandindo um pouco as outras respostas, aqui está um pequeno exemplo de como seus scripts de linha de comando podem ter problemas pelo uso incauto de /usr/bin/envlinhas shebang:

$ /usr/local/bin/python -V
Python 2.6.4
$ /usr/bin/python -V
Python 2.5.1
$ cat my_script.py 
#!/usr/bin/env python
import json
print "hello, json"
$ PATH=/usr/local/bin:/usr/bin
$ ./my_script.py 
hello, json
$ PATH=/usr/bin:/usr/local/bin
$ ./my_script.py 
Traceback (most recent call last):
  File "./my_script.py", line 2, in <module>
    import json
ImportError: No module named json

O módulo json não existe no Python 2.5.

Uma maneira de se proteger contra esse tipo de problema é usar os nomes de comando python com versão que geralmente são instalados com a maioria dos Pythons:

$ cat my_script.py 
#!/usr/bin/env python2.6
import json
print "hello, json"

Se você apenas precisa distinguir entre Python 2.xe Python 3.x, as versões recentes do Python 3 também fornecem um python3nome:

$ cat my_script.py 
#!/usr/bin/env python3
import json
print("hello, json")
Ned Deily
fonte
27
Hmm, não foi isso que tirei desse post.
Glenn Jackman
1
Diferença entre local e global. Se which pythonretornos /usr/bin/python, um caminho de diretório local poderia ser codificado: #!/usr/bin/python. Mas isso é menos flexível do que o #!/usr/bin/env pythonque tem uma aplicação global.
noobninja
85

Para executar o script python, precisamos dizer ao shell três coisas:

  1. Que o arquivo é um script
  2. Qual intérprete queremos executar o script
  3. O caminho do referido intérprete

O shebang #!realiza (1.). O shebang começa com um #porque o #personagem é um marcador de comentário em muitas linguagens de script. O conteúdo da linha shebang é, portanto, automaticamente ignorado pelo intérprete.

O envcomando realiza (2.) e (3.). Para citar "grawity",

Um uso comum do envcomando é iniciar intérpretes, usando o fato de que o env pesquisará $ PATH pelo comando que é solicitado a iniciar. Como a linha shebang exige que um caminho absoluto seja especificado e como a localização de vários intérpretes (perl, bash, python) pode variar muito, é comum usar:

#!/usr/bin/env perl  em vez de tentar adivinhar se é / bin / perl, / usr / bin / perl, / usr / local / bin / perl, / usr / local / pkg / perl, / fileserver / usr / bin / perl ou / home / MrDaniel / usr / bin / perl no sistema do usuário ...

Por outro lado, o env está quase sempre em / usr / bin / env. (Exceto nos casos em que não é; alguns sistemas podem usar / bin / env, mas essa é uma ocasião bastante rara e ocorre apenas em sistemas não Linux.)

Rose Perrone
fonte
1
"grawity" onde?
Pacerier 16/08/19
44

Talvez sua pergunta seja neste sentido:

Se você deseja usar: $python myscript.py

Você não precisa dessa linha. O sistema chamará python e, em seguida, o interpretador python executará seu script.

Mas se você pretende usar: $./myscript.py

Chamando-o diretamente como um programa normal ou script bash, você precisa escrever essa linha para especificar ao sistema qual programa usar para executá-lo (e também torná-lo executável chmod 755)

user3765197
fonte
ou você pode escrever myscript.py python3
shanker Vijay
44

A execchamada do sistema do kernel Linux entende shebangs ( #!) nativamente

Quando você faz no bash:

./something

no Linux, este chama a execchamada de sistema com o caminho ./something.

Essa linha do kernel é chamada no arquivo passado para exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

Ele lê os primeiros bytes do arquivo e os compara #!.

Se a comparação for verdadeira, o restante da linha será analisado pelo kernel do Linux, que fará outra execchamada com o caminho /usr/bin/env pythone o arquivo atual como o primeiro argumento:

/usr/bin/env python /path/to/script.py

e isso funciona para qualquer linguagem de script que use # como caractere de comentário.

E sim, você pode fazer um loop infinito com:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

O Bash reconhece o erro:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! por acaso é legível por humanos, mas isso não é necessário.

Se o arquivo execfosse iniciado com bytes diferentes, a chamada do sistema usaria um manipulador diferente. O outro manipulador interno mais importante é os arquivos executáveis ​​do ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305, que verifica se há bytes 7f 45 4c 46(que também são humanos legível para .ELF). Vamos confirmar que, lendo os 4 primeiros bytes de /bin/ls, que é um executável ELF:

head -c 4 "$(which ls)" | hd 

resultado:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

Portanto, quando o kernel vê esses bytes, ele pega o arquivo ELF, coloca-o na memória corretamente e inicia um novo processo com ele. Veja também: Como o kernel obtém um arquivo binário executável em execução no linux?

Por fim, você pode adicionar seus próprios manipuladores shebang com o binfmt_miscmecanismo Por exemplo, você pode adicionar um manipulador personalizado para .jararquivos . Esse mecanismo ainda suporta manipuladores por extensão de arquivo. Outro aplicativo é executar de forma transparente executáveis ​​de uma arquitetura diferente com o QEMU .

Eu não acho que o POSIX especifique shebangs, no entanto: https://unix.stackexchange.com/a/346214/32558 , embora mencione nas seções de justificativa e na forma "se scripts executáveis ​​são suportados pelo sistema, algo pode acontecer". O macOS e o FreeBSD também parecem implementá-lo.

PATH busca motivação

Provavelmente, uma grande motivação para a existência de shebangs é o fato de que no Linux, geralmente queremos executar comandos da seguinte PATHmaneira:

basename-of-command

ao invés de:

/full/path/to/basename-of-command

Mas então, sem o mecanismo shebang, como o Linux saberia como iniciar cada tipo de arquivo?

Codificar a extensão nos comandos:

 basename-of-command.py

ou implementando a pesquisa PATH em todos os intérpretes:

python basename-of-command

seria uma possibilidade, mas isso tem o grande problema de que tudo quebra se decidirmos refatorar o comando para outro idioma.

Shebangs resolve esse problema lindamente.

Ciro Santilli adicionou uma nova foto
fonte
39

Tecnicamente, em Python, isso é apenas uma linha de comentário.

Essa linha é usada apenas se você executar o script py no shell (na linha de comando). Isso é conhecido como " Shebang !" , e é usado em várias situações, não apenas nos scripts Python.

Aqui, ele instrui o shell a iniciar uma versão específica do Python (para cuidar do restante do arquivo.

mjv
fonte
O shebang é um conceito Unix. Vale a pena mencionar que ele funciona no Windows também se você instalou o iniciador do Pythonpy.exe . Isso faz parte de uma instalação padrão do Python.
florisla
38

O principal motivo para fazer isso é tornar o script portátil em ambientes de sistema operacional.

Por exemplo, em mingw, os scripts python usam:

#!/c/python3k/python 

e sob a distribuição GNU / Linux, é:

#!/usr/local/bin/python 

ou

#!/usr/bin/python

e no melhor sistema comercial Unix sw / hw de todos (OS / X), é:

#!/Applications/MacPython 2.5/python

ou no FreeBSD:

#!/usr/local/bin/python

No entanto, todas essas diferenças podem tornar o script portátil através de todos, usando:

#!/usr/bin/env python
Jonathan Cline IEEE
fonte
2
No MacOSX, também é /usr/bin/python. No Linux, o Python instalado pelo sistema também é quase certamente /usr/bin/python(nunca vi mais nada e não faria sentido). Observe que pode haver sistemas que não possuem /usr/bin/env.
28412 Albert Albert
1
Se você estiver no OSX e usar o Homebrew e seguir as instruções de instalação padrão, ele estará em #! / Usr / local / bin / python
será
@ Jean-PaulCalderone: Veja a resposta de saaj abaixo.
pydsigner
Atualização para o ano de 2018: o Bare pythonnão é tão portátil, é o interpretador Python padrão de distribuição. Arch Linux padrão para Python 3 por muito tempo e pode distribuições estão pensando sobre isso também, porque Python 2 é suportado somente até 2020.
mati865
22

Provavelmente faz sentido enfatizar uma coisa que a maioria perdeu, o que pode impedir a compreensão imediata. Quando você digita o pythonterminal, normalmente não fornece um caminho completo. Em vez disso, o executável é procurado na PATHvariável de ambiente. Por sua vez, quando você deseja executar um programa Python diretamente, /path/to/app.pydeve-se dizer ao shell qual intérprete usar (via hashbang , o que os outros colaboradores estão explicando acima).

Hashbang espera o caminho completo para um intérprete. Portanto, para executar seu programa Python diretamente, você deve fornecer o caminho completo para o binário Python, que varia significativamente, especialmente considerando o uso do virtualenv . Para lidar com a portabilidade, o truque /usr/bin/envé usado. O último é originalmente destinado a alterar o ambiente no local e executar um comando nele. Quando nenhuma alteração é fornecida, ele executa o comando no ambiente atual, o que resulta efetivamente na mesma PATHpesquisa que faz o truque.

Origem do unix stackexchange

saaj
fonte
14

Esta é uma convenção de shell que informa ao shell qual programa pode executar o script.

#! / usr / bin / env python

resolve um caminho para o binário Python.

Frank Krueger
fonte
9

Você pode tentar esse problema usando o virtualenv

Aqui está o test.py

#! /usr/bin/env python
import sys
print(sys.version)

Crie ambientes virtuais

virtualenv test2.6 -p /usr/bin/python2.6
virtualenv test2.7 -p /usr/bin/python2.7

ative cada ambiente e verifique as diferenças

echo $PATH
./test.py
Sercan
fonte
9

Apenas especifica qual intérprete você deseja usar. Para entender isso, crie um arquivo por meio do terminal touch test.py, e digite o arquivo a seguir:

#!/usr/bin/env python3
print "test"

e faça chmod +x test.pypara tornar seu script executável. Depois disso, ./test.pyvocê deve receber um erro dizendo:

  File "./test.py", line 2
    print "test"
               ^
SyntaxError: Missing parentheses in call to 'print'

porque python3 não suporta o operador de impressão.

Agora vá em frente e altere a primeira linha do seu código para:

#!/usr/bin/env python2

e funcionará, imprimindo testem stdout, porque python2 suporta o operador de impressão. Então, agora você aprendeu como alternar entre intérpretes de script.

Pavel
fonte
9

Parece-me que os arquivos executam o mesmo sem essa linha.

Se sim, então talvez você esteja executando o programa Python no Windows? O Windows não usa essa linha; em vez disso, usa a extensão de nome de arquivo para executar o programa associado à extensão de arquivo.

No entanto, em 2011, foi desenvolvido um "iniciador Python" que (até certo ponto) imita esse comportamento do Linux para Windows. Isso se limita apenas à escolha de qual interpretador Python é executado - por exemplo, para selecionar entre Python 2 e Python 3 em um sistema em que os dois estão instalados. O iniciador é opcionalmente instalado como py.exepela instalação do Python e pode ser associado aos .pyarquivos, para que o iniciador verifique essa linha e, por sua vez, inicie a versão especificada do interpretador do Python.

Craig McQueen
fonte
6
Ele também pode estar usando $ python myscript.py.
Sinan
Cometi o erro de não ter a linha e usei python script.py, e um dia eu fiz ./myscript.py e tudo parou de funcionar, depois percebi que o sistema estava olhando o arquivo como um script de shell em vez de um script python.
Guagua
8

Isso significa mais informações históricas do que uma resposta "real".

Lembre-se de que antigamente você tinha MUITO UNIX, como sistemas operacionais, cujos designers tinham sua própria noção de onde colocar coisas e, às vezes, não incluíam Python, Perl, Bash ou muitas outras coisas de GNU / Código Aberto . .

Isso aconteceu mesmo com diferentes distribuições Linux. No Linux - pré-FHS [1], você pode ter python em / usr / bin / ou / usr / local / bin /. Ou pode não ter sido instalado, então você construiu o seu próprio e o colocou em ~ / bin

O Solaris foi o pior em que já trabalhei, parcialmente como a transição do Berkeley Unix para o System V. Você pode terminar com coisas em / usr /, / usr / local /, / usr / ucb, / opt / etc. Isso pode fazer com que para alguns realmente caminhos longos. Tenho lembranças das coisas do Sunfreeware.com instalando cada pacote em seu próprio diretório, mas não me lembro se ele vinculou os binários em / usr / bin ou não.

Ah, e algumas vezes o / usr / bin estava em um servidor NFS [2].

Portanto, o envutilitário foi desenvolvido para solucionar isso.

Então você poderia escrever #!/bin/env interpretere, desde que o caminho fosse apropriado, as coisas tinham uma chance razoável de correr. Obviamente, era razoável (para Python e Perl) que você também definisse as variáveis ​​ambientais apropriadas. Para o bash / ksh / zsh, apenas funcionou.

Isso foi importante porque as pessoas estavam passando scripts de shell (como perl e python) e se você codificasse / usr / bin / python na sua estação de trabalho Red Hat Linux, isso seria ruim para um SGI ... bem, não , Acho que o IRIX colocou o python no lugar certo. Mas em uma estação Sparc pode não funcionar.

Sinto falta da minha estação sparc. Mas não muito. Ok, agora você me deixou passeando no E-Bay. Bastagens.

[1] Padrão de hierarquia do sistema de arquivos. https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2] Sim, e às vezes as pessoas ainda fazem coisas assim. E não, eu não usava nem nabo nem cebola no cinto.

Petro
fonte
5

Se você estiver executando seu script em um ambiente virtual, por exemplo venv, a execução which pythondurante o trabalho venvexibirá o caminho para o interpretador Python:

~/Envs/venv/bin/python

Observe que o nome do ambiente virtual está incorporado no caminho para o interpretador Python. Portanto, a codificação desse caminho no seu script causará dois problemas:

  • Se você enviar o script para um repositório, está forçando outros usuários a terem o mesmo nome de ambiente virtual . Isto é, se eles identificarem o problema primeiro.
  • Você não poderá executar o script em vários ambientes virtuais, mesmo se todos os pacotes necessários forem necessários em outros ambientes virtuais.

Portanto, para adicionar à resposta de Jonathan , o shebang ideal é #!/usr/bin/env python, não apenas para portabilidade entre sistemas operacionais, mas também para portabilidade em ambientes virtuais!

Bruce Wayne
fonte
3

Considerando os problemas de portabilidade entre python2e python3, você sempre deve especificar uma das versões, a menos que seu programa seja compatível com ambas.

Algumas distribuições são frete pythonligada simbolicamente a python3por um tempo agora - não dependem de pythonserpython2 .

Isso é enfatizado pelo PEP 394 :

Para tolerar diferenças entre plataformas, todo o novo código que precisa chamar o interpretador Python não deve especificar python, mas sim especificar python2 ou python3 (ou as versões mais específicas python2.xe python3.x; consulte as Notas sobre migração ) . Essa distinção deve ser feita em shebangs, ao invocar a partir de um script de shell, ao invocar através da chamada system () ou ao invocar em qualquer outro contexto.

Zulan
fonte
2

Ele informa ao intérprete com qual versão do python executar o programa quando você possui várias versões do python.

Katie T
fonte
0

Permite selecionar o executável que você deseja usar; o que é muito útil se você tiver várias instalações python e módulos diferentes em cada uma delas e desejar escolher. por exemplo

#!/bin/sh
#
# Choose the python we need. Explanation:
# a) '''\' translates to \ in shell, and starts a python multi-line string
# b) "" strings are treated as string concat by python, shell ignores them
# c) "true" command ignores its arguments
# c) exit before the ending ''' so the shell reads no further
# d) reset set docstrings to ignore the multiline comment code
#
"true" '''\'
PREFERRED_PYTHON=/Library/Frameworks/Python.framework/Versions/2.7/bin/python
ALTERNATIVE_PYTHON=/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
FALLBACK_PYTHON=python3

if [ -x $PREFERRED_PYTHON ]; then
    echo Using preferred python $ALTERNATIVE_PYTHON
    exec $PREFERRED_PYTHON "$0" "$@"
elif [ -x $ALTERNATIVE_PYTHON ]; then
    echo Using alternative python $ALTERNATIVE_PYTHON
    exec $ALTERNATIVE_PYTHON "$0" "$@"
else
    echo Using fallback python $FALLBACK_PYTHON
    exec python3 "$0" "$@"
fi
exit 127
'''

__doc__ = """What this file does"""
print(__doc__)
import platform
print(platform.python_version())
Neil McGill
fonte
-8

isto diz ao script onde está o diretório python!

#! /usr/bin/env python
Bhargav Rao
fonte