Como posso usar variáveis ​​de ambiente no meu shebang?

34

Eu tenho um script Python que precisa ser executado com uma instalação python específica. Existe uma maneira de criar um shebang para que ele funcione $FOO/bar/MyCustomPython?

eric.frederich
fonte

Respostas:

29

A linha shebang é muito limitada. Sob muitas variantes do unix (incluindo Linux), você pode ter apenas duas palavras: um comando e um único argumento. Também há frequentemente uma limitação de comprimento.

A solução geral é escrever um pequeno invólucro de casca. Nomeie o script Python foo.pye coloque o shell script ao lado foo.pye chame-o foo. Essa abordagem não requer nenhum cabeçalho específico no script Python.

#!/bin/sh
exec "$FOO/bar/MyCustomPython" "$0.py" "$@"

Outra abordagem tentadora é escrever um script wrapper como o descrito acima e colocar #!/path/to/wrapper/scriptcomo a linha shebang no script Python. No entanto, a maioria dos unices não suporta encadeamento de scripts shebang, portanto, isso não funcionará.

Se MyCustomPythonestava no $PATH, você pode usar envpara procurar:

#!/usr/bin/env MyCustomPython
import 

Outra abordagem é organizar para que o script seja um script shell válido (que carrega o interpretador Python correto em si mesmo) e um script válido na linguagem de destino (aqui Python). Isso requer que você encontre uma maneira de escrever um script em dois idiomas para o seu idioma de destino. No Perl, isso é conhecido como if $running_under_some_shell.

#!/bin/sh
eval 'exec "$FOO/bar/MyCustomPerl" -wS $0 ${1+"$@"}'
    if $running_under_some_shell;
use 

Aqui está uma maneira de obter o mesmo efeito no Python. No shell, "true"é o trueutilitário, que ignora seus argumentos (duas cadeias de caracteres únicos :e ') e retorna um valor verdadeiro. No Python, "true"é uma string que é verdadeira quando interpretada como booleana; portanto, esta é uma ifinstrução que é sempre verdadeira e executa uma literal de string.

#!/bin/sh
if "true" : '''\'
then
exec "$FOO/bar/MyCustomPython" "$0" "$@"
exit 127
fi
'''
import 

O código Rosetta possui scripts de dois idiomas em vários outros idiomas.

Gilles 'SO- parar de ser mau'
fonte
11
@ Chris: tudo está dentro de aspas simples. Por favor, explique…
Stéphane Gimenez
11
Você é um maldito bruxo ... Isso é ótimo. Usei isso para editar a PYTHONPATHvariável env para carregar módulos de um local não padrão para um script CGI em um sistema ao qual não tinha acesso root. Poupança de vida. Obrigado novamente.
Wspurgin
12

As linhas Shebang não sofrem expansão variável; portanto, você não pode usá- $FOO/MyCustomPythonlo, pois ele procuraria por um executável chamado dollar-FOO -...

Uma alternativa é fazer com que seu shebang aponte para um script de shell como intérprete, e esse script de shell pode usar variáveis ​​de ambiente para localizar o correto e executá-lo.

Exemplo: crie um mypython.shscript /usr/local/bin(ou qualquer outro diretório no seu $PATH), com o seguinte conteúdo:

#! /bin/sh
PYTHON="$FOO/bar/MyCustomPython"
exec "$PYTHON" "$@"

então você pode usar esta linha shebang para executar um script Python MyCustomPythonatravés de mypython.sh:

#!/usr/bin/env mypython.sh
Riccardo Murri
fonte
6
Use $python, não $PYTHONpor convenção, capitalizamos variáveis ​​de ambiente (PAGER, EDITOR, SHELL ... $PYTHONem seu script não é uma variável de ambiente) e variáveis ​​internas do shell (BASH_VERSION, RANDOM, ...). Todos os outros nomes de variáveis ​​devem conter pelo menos uma letra minúscula. Esta convenção evita a substituição acidental de variáveis ​​ambientais e internas. Além disso, não use extensões para seus scripts. Os scripts definem novos comandos que você pode executar, e os comandos geralmente não recebem extensões.
Chris Baixo
4

Você pode usar o caminho absoluto para a instalação customizada do python ou pode colocá-lo no seu $PATHe use #!/usr/bin/env [command]. Caso contrário, escreva um wrapper para ele e use-o execpara substituir a imagem do processo, como um exemplo:

#!/bin/bash
exec "$ENV/python" "$@"
Chris Down
fonte
3

Primeiro, determine como a sua versão do Python é diferente do Python padrão já instalado (por exemplo, um módulo adicional) e, em seguida, no início do programa 'switch', como o Python é chamado:

#!/usr/bin/python
import os
import sys
try:
    import MyCustomModule
except ImportError:
    # only need to use expandvar if you know the string below has an envvar
    custprog = os.path.expandvar("$FOO/bar/MyCustomPython")
    os.execv(custprog, sys.argv) # call alternate python with the same arguments
    raise SystemExit('could not call %s' % custprog)
# if we get here, then we have the correct python interpreter

Isso deve permitir que o programa seja chamado por qualquer outra instância do Python. Faço algo semelhante para uma instância com a biblioteca sql interna que não pode ser importada como um módulo no python do sistema.

Arcege
fonte
0

Se você pode substituir $FOOpor $FOO/bar/MyCustomPythonopções de caminho concreto, pode dizer à envlinha shebang onde procurar sua versão personalizada do Python, definindo diretamente um personalizado PATH.

#!/usr/bin/env PATH="/path1/to/MyCustomPython:/path2/to/MyCustomPython" python

Editar : parece funcionar apenas sem aspas na atribuição de valor PATH:

#!/usr/bin/env PATH=/path1/to/MyCustomPython:/path2/to/MyCustomPython python
chan
fonte
5
Cuidado que isso não funciona em todas as variantes do unix (por exemplo, não no Linux). E você está removendo todos os diretórios existentes do caminho, o que provavelmente causará problemas na linha.
Gilles 'SO- stop being evil'
11
Melhor expandir PATHassim:PATH=$PATH:/path/to/whatever...
Bengt