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

6

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

SHEBANG="#!/bin/sh"

Minha pergunta é se posso usar essa variável em outro script como este:

$SHEBANG
# other stuff...
    
por Marco Favorito 24.11.2017 / 21:24

3 respostas

11

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

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

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

  • Você pode empurrar as coisas ainda mais nessa linha se puder confiar no bash presente: um script cuja primeira linha é

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

    funcionará para a maioria dos intérpretes; O Bash é usado como uma etapa intermediária para que a substituição do processo possa ser usada para tail pule a primeira linha para nós, para que não acabemos em uma Loop infinito. Alguns intérpretes exigem que o arquivo seja procurado e eles não aceitarão um pipeline como este.

Vale a pena considerar se isso é realmente algo que você quer fazer. Além de quaisquer preocupações de segurança, não é muito útil: alguns scripts podem ter seus intérpretes alterados assim, e onde eles podem, quase certamente, ficar melhor com uma lógica mais restrita que gerou a correta para a situação. (talvez um script de shell no meio, que chama o interpretador com o caminho para o script real como um argumento).

* Nem sempre; Em certas circunstâncias, algumas shells irão ler elas mesmas, mas elas ainda não farão expansão variável.

    
por 24.11.2017 / 21:56
3

Eu não sei se este é o caso de uso que você tem em mente, mas uma coisa que você pode fazer é

#!/usr/bin/env python

para que seu script use o primeiro python em $PATH em vez de precisar codificar /usr/bin/python , o que pode não funcionar bem em algum sistema em que a "boa" versão do python está em /usr/local/bin ou /opt ou qualquer outra coisa.

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 no arquivo) apenas trata a linha #! como um comentário. Esse é o ponto do design #! , porque muitos idiomas usam # como um caractere de comentário.

Para estender isso, você pode escrever um programa poliglota que inicialmente é executado sob #!/bin/sh , mas depois descobre qual interpretador usar realmente e invoca isso no mesmo script.

Acontece que já existe uma página da web com truques shebang multilinhas para muitos idiomas, inclusive para idiomas compilados (o script alimenta parte de si mesmo para um compilador e executa a saída).

A página perlrun man 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 script /bin/sh , eval executa perl -x -wS no seu script, transmitindo seus argumentos.

Quando o Perl o executa, o eval é controlado pelo if 0; na linha seguinte, por isso nunca é executado. Ao contrário de sh , as instruções Perl não são finalizadas por uma nova linha.

Você poderia tornar isso mais complexo, usando "$SHEBANG" em vez de perl , ou talvez até rodando vários comandos sh para selecionar um intérprete perl para usar.

    
por 25.11.2017 / 03:54
1

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 o arquivo \$shell_var como uma "variável" para definir o shell que você deseja.

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

  3. Como o dash não suporta function f { echo hello world; } syntax , ele produziu o erro function: not found , mas alterou o shell para bash no arquivo \$shell_var não terá mais erros.

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

por 24.11.2017 / 23:26