Posso usar um conteúdo variável como shebang?

Posso usar um conteúdo variável como shebang?

Eu tenho um script de shell no qual desejo adicionar um shebang. Dada uma variável definida da seguinte forma:

SHEBANG="#!/bin/sh"

Minha dúvida é se posso usar essa variável em outro script como este:

$SHEBANG
# other stuff...

Responder1

Não. A linha shebang é processada pelo seu kernel ou biblioteca do sistema* e não processa variáveis ​​do shell.

  • Se você quisesse usar uma variável de ambiente como intérprete por algum motivo, você poderia criar um executável "trampolim" (não um script!) que seria gerado novamente $SHEBANG "$@". Você precisaria omitir o #!da variável neste caso; #!só é útil quando são literalmente os dois primeiros bytes de um arquivo.

  • Outra opção é aproveitaro fato de que scripts sem linhas shebang serão (geralmente) executados como scripts de shell com um shou o shell de chamada. Se houver uma sintaxe de comentário para o seu alvo shebang que se sobreponha de forma útil à sintaxe do shell, você poderá fazê-la funcionar. Esta estratégia é ou costumava ser comum com intérpretes Lisp. No caso do seu exemplo, onde o intérprete também está sh, isso não é viável.

  • Você pode ir ainda mais longe nessa linha se puder confiar em bashestar presente: um script cuja primeira linha é

    exec /bin/bash -c 'exec "$SHEBANG" <(tail -n +2 "$0")' "$0" "$@"
    

    funcionará para a maioria dos intérpretes; Bash é usado como uma etapa intermediária para quesubstituição de processopode ser usado para tertailpule a primeira linha para nós, para que não terminemos em um loop infinito. Alguns intérpretes exigem que o arquivo seja pesquisável e não aceitam um pipeline como este.


Vale a pena considerar se isso é realmente algo que você deseja fazer. Além de quaisquer preocupações de segurança, simplesmente não é muito útil: poucos scripts podem ter seus intérpretes alterados dessa maneira e, onde puderem, você quase certamente estaria melhor com uma lógica mais restrita que gerasse a lógica correta para a situação (talvez um script de shell intermediário, que chame o intérprete com o caminho para o script real como argumento).


* Nem sempre; em certas circunstâncias, alguns shells irão lê-lo sozinhos, mas ainda assim não farão expansão variável.

Responder2

Não sei se este é o caso de uso que você tem em mente, mas uma coisa vocêpodefazer é

#!/usr/bin/env python

então seu script usa o primeiro pythonem $PATHvez de precisar codificar /usr/bin/python, o que pode não funcionar bem em algum sistema onde a versão "boa" do python está /usr/local/binou /optalgo assim.

Por que é melhor usar "#!/usr/bin/env NAME" em vez de "#!/path/to/NAME" como meu shebang?


Isso não cria um loop infinito porque o interpretador Python (que envinvoca o arquivo) apenas trata a #!linha como um comentário. Esse é o objetivo do #!design, porque muitos idiomas usam #como caractere de comentário.

Para estender isso,você poderia escrever um programa poliglotaque inicialmente é executado em #!/bin/sh, mas depois descobre qual intérprete realmente usar e o invoca no mesmo script.

Acontece que já existeuma página da web com truques shebang multilinhapara muitas linguagens, inclusive para linguagens compiladas (o script alimenta parte de si mesmo em um compilador e executa a saída).


A perlrunpágina de manual fornece este exemplo como uma alternativa para #!/usr/bin/env perl:

    #!/bin/sh
    #! -*-perl-*-
    eval 'exec perl -x -wS $0 ${1+"$@"}'
        if 0;

    your perl script goes here

Quando executado como um /bin/shscript, evalé executado perl -x -wSno seu script, passando seus argumentos.

Quando Perl o executa, o evalé controlado pelo if 0;na próxima linha, portanto nunca é executado. Ao contrário sh, as instruções Perl não terminam com uma nova linha.

Você poderia tornar isso mais complexo, usando "$SHEBANG"em vez de perl, ou talvez até mesmo executando vários shcomandos para selecionar um interpretador Perl a ser usado.

Responder3

Acho que é isso que você quer:

xb@dnxb:/tmp$ cat /tmp/hole.sh
#!/usr/bin/$shell_var
echo 1
readlink "/proc/$$/exe"
echo 2
function f { echo hello world; } 
echo 3 
xb@dnxb:/tmp$ chmod +x /tmp/hole.sh
xb@dnxb:/tmp$ sudo vi /usr/bin/\$shell_var 
xb@dnxb:/tmp$ cat /usr/bin/\$shell_var 
#!/bin/bash
/bin/dash -- "$@"
xb@dnxb:/tmp$ sudo chmod +x /usr/bin/\$shell_var
xb@dnxb:/tmp$ ./hole.sh
1
/bin/dash
2
./hole.sh: 5: ./hole.sh: function: not found
3
xb@dnxb:/tmp$ sudo vi /usr/bin/\$shell_var 
xb@dnxb:/tmp$ cat /usr/bin/\$shell_var 
#!/bin/bash
/bin/bash -- "$@"
xb@dnxb:/tmp$ ./hole.sh 
1
/bin/bash
2
3
xb@dnxb:/tmp$ 

Explicação:

  1. Use \$shell_varfile como uma "variável" para definir o shell desejado.

  2. readlink "/proc/$$/exe"imprimirá o shell atual.

  3. Como o Dash não suportafunction f { echo hello world; }sintaxe, então produziu um erro function: not found, mas alterar o shell para bash no \$shell_vararquivo não causará mais erros.

  4. O caminho completo do arquivo (ou caminho relativo se ./hole.sh for executado em /tmp) passará como $@para o \$shell_vararquivo e /tmp/hole.shserá executado dentro de \$shell_var.

informação relacionada