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
sh
ou 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
bash
estar 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 ter
tail
pule 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 python
em $PATH
vez 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/bin
ou /opt
algo 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 env
invoca 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 perlrun
pá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/sh
script, eval
é executado perl -x -wS
no 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 sh
comandos 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:
Use
\$shell_var
file como uma "variável" para definir o shell desejado.Como o Dash não suporta
function f { echo hello world; }
sintaxe, então produziu um errofunction: not found
, mas alterar o shell para bash no\$shell_var
arquivo não causará mais erros.O caminho completo do arquivo (ou caminho relativo se ./hole.sh for executado em /tmp) passará como
$@
para o\$shell_var
arquivo e/tmp/hole.sh
será executado dentro de\$shell_var
.