Existe alguma maneira de escolher dinamicamente o intérprete que está executando um script? Eu tenho um script que estou executando em dois sistemas diferentes, e o intérprete que eu quero usar está localizado em locais diferentes nos dois sistemas. O que eu preciso fazer é mudar a linha de hashbang toda vez que mudar. Eu gostaria de fazer algo que seja o equivalente lógico disso (eu percebo que essa construção exata é impossível):
if running on system A:
#!/path/to/python/on/systemA
elif running on system B:
#!/path/on/systemB
#Rest of script goes here
Ou melhor ainda, é isso, para tentar usar o primeiro intérprete e, se não encontrar, usa o segundo:
try:
#!/path/to/python/on/systemA
except:
#!path/on/systemB
#Rest of script goes here
Obviamente, posso executá-lo como
/path/to/python/on/systemA myscript.py
ou
/path/on/systemB myscript.py
dependendo de onde estou, mas na verdade tenho um script de wrapper que é iniciado myscript.py
, portanto, gostaria de especificar o caminho para o interpretador python de forma programática e não manualmente.
if
condição não é uma opção para você?if something; then /bin/sh restofscript.sh elif...
Respostas:
Não, isso não vai funcionar. Os dois caracteres
#!
absolutamente precisam ser os dois primeiros no arquivo (como você especificaria o que interpretou a instrução if?). Isso constitui o "número mágico" que aexec()
família de funções detecta quando determina se um arquivo que está prestes a executar é um script (que precisa de um intérprete) ou um arquivo binário (que não).O formato da linha shebang é bastante rigoroso. Ele precisa ter um caminho absoluto para um intérprete e, no máximo, um argumento para ele.
O que você pode fazer é usar
env
:Agora, o caminho para isso
env
é geralmente/usr/bin/env
, mas tecnicamente isso não garante.Isso permite que você ajustar a
PATH
variável de ambiente em cada sistema para queinterpreter
(seja elebash
,python
ouperl
ou o que você tem) é encontrado.Uma desvantagem dessa abordagem é que será impossível passar um argumento de maneira portável para o intérprete.
Isso significa que
e
é improvável que funcione em alguns sistemas.
Outra abordagem óbvia é usar as ferramentas automáticas GNU (ou algum sistema de modelos mais simples) para encontrar o intérprete e colocar o caminho correto no arquivo em uma
./configure
etapa, que seria executada na instalação do script em cada sistema.Pode-se também recorrer à execução do script com um intérprete explícito, mas é obviamente isso que você está tentando evitar:
fonte
#!
precisa acontecer no começo, já que não é o shell que processa essa linha. Eu queria saber se existe uma maneira de colocar lógica dentro da linha hashbang que seria equivalente ao if / else. Eu também esperava evitar mexer com o meu,PATH
mas acho que essas são minhas únicas opções.#!/usr/bin/awk
, você pode fornecer exatamente um argumento, como#!/usr/bin/awk -f
. Se o binário que você está apontando forenv
, o argumento é o binário que você está pedindoenv
para procurar, como em#!/usr/bin/env awk
./usr/bin/env
com o argumento únicoawk -f
.foo.awk
com a linha hashbang#!/usr/bin/env awk -f
e o chama./foo.awk
, no Linux, o queenv
vê são os dois parâmetrosawk -f
e./foo.awk
. Na verdade, ele procura/usr/bin/awk -f
(etc.) com um espaço.Você sempre pode criar um script de wrapper para encontrar o intérprete correto para o programa real:
Salve o wrapper nos usuários
PATH
comoprogram
e colocar o programa real de lado ou com outro nome.Eu usei
#!/bin/bash
no hashbang por causa daflags
matriz. Se você não precisar armazenar um número variável de sinalizadores ou algo semelhante e puder ficar sem ele, o script deverá funcionar de maneira portável#!/bin/sh
.fonte
exec "$interpreter" "${flags[@]}" "$script" "$@"
também usado para manter a árvore de processos mais limpa. Também propaga o código de saída.exec
.#!/bin/sh
seria melhor em vez de#!/bin/bash
? Mesmo que/bin/sh
seja um link simbólico para um shell diferente, ele deve existir na maioria dos sistemas (se não todos) * nix, além de forçar o autor do script a criar um script portátil, em vez de cair em basismos.flags
é um recurso não padrão, mas é útil o suficiente para armazenar um número variável de sinalizadores, então decidi mantê-lo.script=/what/ever; something && exec this "$script" "$@"; exec that "$script" -x -y "$@"
. Você também pode adicionar verificação de erros para falhas de execução.Você também pode escrever um poliglota (combinar dois idiomas). / bin / sh é garantido que existe.
Isso tem a desvantagem do código feio e talvez alguns
/bin/sh
s possam se confundir. Mas pode ser usado quandoenv
não existe ou existe em outro lugar que não seja / usr / bin / env. Também pode ser usado se você quiser fazer uma seleção bastante sofisticada.A primeira parte do script determina qual intérprete usar quando executado com / bin / sh como intérprete, mas é ignorado quando executado pelo intérprete correto. Use
exec
para impedir que o shell execute mais do que a primeira parte.Exemplo de Python:
fonte
exec "$interpreter" "$0" "$@"
que o nome do script seja o próprio interpretador. (E então espero que ninguém mentiu ao configurar$0
.)#!
, Scala ignora tudo, até uma correspondência!#
; isso permite que você coloque código de script arbitrariamente complexo em um idioma arbitrário e, em seguida,exec
o mecanismo de execução do Scala com o script.Prefiro as respostas de Kusalananda e ilkkachu, mas aqui está uma resposta alternativa que faz mais diretamente o que a pergunta estava pedindo, simplesmente porque foi feita.
Observe que você só pode fazer isso quando o intérprete permite escrever código no primeiro argumento. Aqui,
-e
e tudo depois que é tomado literalmente como 1 argumento para ruby. Pelo que sei, você não pode usar o bash para o código shebang, porquebash -c
exige que o código esteja em um argumento separado.Eu tentei fazer o mesmo com python para código shebang:
mas fica muito tempo e o linux (pelo menos na minha máquina) trunca o shebang para 127 caracteres. Por favor, desculpe o uso de
exec
para inserir novas linhas, pois o python não permite trechos de tentativa ouimport
s sem novas linhas.Não tenho certeza de como isso é portátil e não o faria em código destinado a ser distribuído. No entanto, é factível. Talvez alguém ache útil para depuração rápida e suja ou algo assim.
fonte
Embora isso não selecione o intérprete no script de shell (ele o seleciona por máquina), é uma alternativa mais fácil se você tiver acesso administrativo a todas as máquinas nas quais está tentando executar o script.
Crie um link simbólico (ou um link físico, se desejado) para apontar para o caminho do intérprete desejado. Por exemplo, no meu sistema, perl e python estão em / usr / bin:
criaria um link simbólico para permitir que o hashbang resolva para / bin / perl, etc. Isso preserva a capacidade de passar parâmetros para os scripts também.
fonte
Fui confrontado com um problema semelhante como esse hoje (
python3
apontando para uma versão do python muito antiga em um sistema) e criei uma abordagem um pouco diferente das discutidas aqui: Use a versão "errada" do python para inicializar no "certo". A limitação é que algumas versões do python precisam ser acessíveis de maneira confiável, mas isso geralmente pode ser alcançado por exemplo#!/usr/bin/env python3
.Então, o que faço é iniciar meu script com:
O que isso faz é:
fonte