Estou em uma situação um pouco interessante, onde tenho um script Python que teoricamente pode ser executado por uma variedade de usuários com uma variedade de ambientes (e PATHs) e em uma variedade de sistemas Linux. Quero que esse script seja executável no maior número possível, sem restrições artificiais. Aqui estão algumas configurações conhecidas:
- Python 2.6 é a versão do sistema em Python, portanto, python, python2 e python2.6 existem em / usr / bin (e são equivalentes).
- Python 2.6 é a versão do sistema Python, como acima, mas o Python 2.7 é instalado ao lado dele como python2.7.
- Python 2.4 é a versão do sistema Python, que meu script não suporta. Em / usr / bin, temos python, python2 e python2.4, que são equivalentes, e python2.5, que o script suporta.
Eu quero executar o mesmo script python executável em todos os três. Seria bom se ele tentasse usar o /usr/bin/python2.7 primeiro, se existir, volte para /usr/bin/python2.6 e depois para /usr/bin/python2.5, depois simplesmente erro se nenhum deles estiver presente. Não estou muito empolgado com isso usando o 2.x mais recente possível, desde que seja capaz de encontrar um dos intérpretes corretos, se presente.
Minha primeira inclinação foi mudar a linha shebang de:
#!/usr/bin/python
para
#!/usr/bin/python2.[5-7]
pois isso funciona bem no bash. Mas a execução do script fornece:
/usr/bin/python2.[5-7]: bad interpreter: No such file or directory
Ok, então eu tento o seguinte, que também funciona no bash:
#!/bin/bash -c /usr/bin/python2.[5-7]
Mas, novamente, isso falha com:
/bin/bash: - : invalid option
Ok, obviamente eu poderia escrever um script de shell separado que encontre o intérprete correto e execute o script python usando o interpretador encontrado. Eu consideraria um incômodo distribuir dois arquivos onde um seria suficiente, desde que seja executado com o intérprete python 2 mais atualizado instalado. Pedir às pessoas que invoquem o intérprete explicitamente (por exemplo, $ python2.5 script.py
) não é uma opção. Confiar no PATH do usuário sendo configurado de uma certa maneira também não é uma opção.
Editar:
A verificação de versão no script Python não funcionará, pois estou usando a instrução "with" que existe no Python 2.6 (e pode ser usada no 2.5 com from __future__ import with_statement
). Isso faz com que o script falhe imediatamente com um SyntaxError hostil ao usuário e me impede de ter a oportunidade de verificar a versão primeiro e emitir um erro apropriado.
Exemplo: (tente isso com um interpretador Python menor que 2.6)
#!/usr/bin/env python
import sys
print "You'll never see this!"
sys.exit()
with open('/dev/null', 'w') as out:
out.write('something')
import sys; sys.version_info()
para verificar se o usuário tem a versão python necessária../script.py
) faria com que o python2.4 o executasse, o que faria com que o seu código detetasse que era a versão errada (e saia, presumivelmente). Mas há um python2.5 perfeitamente bom que poderia ter sido usado como intérprete!exec
, se houver, imprima um erro.execve
). Os argumentos são literais de string, sem globbing, sem regexps. É isso aí. Mesmo se o primeiro argumento for "/ bin / bash" e a segunda opção ("-c ..."), essas opções não serão analisadas por um shell. Eles são entregues sem tratamento para o executável do bash, e é por isso que você obtém esses erros. Além disso, o shebang só funciona se for no começo. Acho que você está sem sorte aqui (com exceção de um script que encontra um interpretador python e o alimenta com um documento HERE, que soa como uma bagunça terrível).Respostas:
Não sou especialista, mas acredito que você não deve especificar a versão exata do python a ser usada e deixar essa opção para o sistema / usuário.
Além disso, você deve usar isso em vez de codificar o caminho para python no script:
ou
É recomendado pelo documento Python em todas as versões:
Em várias distribuições, o Python pode ser instalado em locais diferentes, portanto, ele
env
será procurado emPATH
. Ele deve estar disponível em todas as principais distribuições Linux e pelo que vejo no FreeBSD.O script deve ser executado com a versão do Python que está no seu PATH e que é escolhida pela sua distribuição *.
Se o seu script é compatível com todas as versões do Python, exceto a 2.4, você deve verificar dentro dele se ele é executado no Python 2.4 e imprimir algumas informações e sair.
Mais para ler
env
.Nota de rodapé
* No Gentoo, existe uma ferramenta chamada
eselect
. Usando-o, você pode definir versões padrão de diferentes aplicativos (incluindo Python) como padrão:fonte
Com base em algumas idéias de alguns comentários, consegui juntar um truque realmente feio que parece funcionar. O script se torna um script bash que envolve um script Python e o passa para um intérprete Python através de um "documento aqui".
No inicio:
O código Python está aqui. Então, no final:
Quando o usuário executa o script, a versão mais recente do Python entre 2.5 e 2.7 é usada para interpretar o restante do script como um documento aqui.
Uma explicação sobre algumas travessuras:
O material de aspas triplas que adicionei também permite que esse mesmo script seja importado como um módulo Python (que eu uso para fins de teste). Quando importado pelo Python, tudo entre a primeira e a segunda aspas simples triplas é interpretado como uma sequência no nível do módulo, e a terceira aspas simples triplas é comentada. O resto é Python comum.
Quando executadas diretamente (como um script bash agora), as duas primeiras aspas simples se tornam uma sequência vazia, e a terceira aspas simples forma outra sequência com a quarta aspas simples, contendo apenas dois pontos. Essa sequência é interpretada pelo Bash como não operacional. Tudo o resto é a sintaxe do Bash para observar os binários Python em / usr / bin, selecionar o último e executar exec, passando o restante do arquivo como um documento aqui. O documento aqui começa com uma citação tripla entre Python contendo apenas um sinal de hash / pound / octothorpe. O restante do script é interpretado como normal até que a linha que lê '# EOF' encerre o documento aqui.
Sinto que isso é perverso, então espero que alguém tenha uma solução melhor.
fonte
# ft=python
.A linha shebang pode especificar apenas um caminho fixo para um intérprete. Existe o
#!/usr/bin/env
truque para procurar o intérprete noPATH
mas é isso. Se você quiser mais sofisticação, precisará escrever algum código de shell do wrapper.A solução mais óbvia é escrever um script de wrapper. Chame o script python
foo.real
e crie um script wrapperfoo
:Se você deseja colocar tudo em um arquivo, geralmente pode torná-lo um poliglota que começa com uma
#!/bin/sh
linha (que será executada pelo shell), mas também é um script válido em outro idioma. Dependendo do idioma, um poliglota pode ser impossível (se#!
causar um erro de sintaxe, por exemplo). No Python, não é muito difícil.(O texto inteiro entre
'''
e'''
é uma seqüência de caracteres Python no nível superior, que não tem efeito. Para o shell, a segunda linha é a''':'
que, após retirar as aspas, é o comando no-op:
.)fonte
# EOF
no final, como nesta resposta . Sua abordagem é basicamente a mesma descrita aqui .Como seus requisitos indicam uma lista conhecida de binários, você pode fazê-lo no Python da seguinte forma. Não funcionaria além de uma versão menor / maior de um dígito do Python, mas não vejo isso acontecer tão cedo.
Executa a versão mais alta localizada no disco da lista crescente e ordenada de pythons com versão, se a versão marcada no binário for maior que a versão atual da execução do python. A "lista crescente de versões ordenada" é a parte importante desse código.
Desculpas pelo meu python arranhado
fonte
from __future__ import with_statement
, que deve ser a primeira coisa no script Python. Acho que você não conhece uma maneira de executar essa ação ao iniciar o novo intérprete?with
s como uma importação normal ?. Umif mypy == 25: from __future__ import with_statement
trabalho extra logo antes de 'coisas normais'? Você provavelmente não precisa do if, se não suportar o 2.4.Você pode escrever um pequeno script bash que verifique o executável phython disponível e o chame com o script como parâmetro. Você pode fazer desse script o destino da linha shebang:
E esse script simplesmente faz (após a pesquisa):
Eu não tinha certeza se o kernel aceitaria esse script indiretamente, mas verifiquei e funciona.
Editar 1
Para tornar essa imperceptível embaraçosa uma boa proposta, finalmente:
É possível combinar os dois scripts em um arquivo. Você acabou de escrever o script python como um documento aqui no script bash (se você alterar o script python, precisará copiar os scripts juntos novamente). Ou você cria um arquivo temporário em, por exemplo, / tmp ou (se o python suportar isso, eu não sei), você fornece o script como entrada para o intérprete:
fonte
$(ls /usr/bin/python?.? | tail -n1 )
mas não consegui usá-lo de maneira inteligente em um shebang.