#! / Bin / sh é lido pelo intérprete?

66

Em bashou sh, acho que tudo o que começa com #é um comentário .

Mas nos bashscripts escrevemos:

#!/bin/bash

E nos scripts Python, há:

#!/bin/python

Isso significa que, #por si só, é um comentário, enquanto #!não é?

Gaurav Sharma
fonte
11
E quando você começar a olhar para os perfis da Apparmor, verá #include. Lá também, o #significado não é um comentário.
4
@ vasa1 Mas o ponto-chave que muitas vezes não é apreciado sobre linhas hashbang no início do shell scripts é que eles são comentários .
Elias Kagan

Respostas:

100

A #!linha é usada antes da execução do script e depois ignorada quando o script é executado.

Você está perguntando qual é a diferença entre uma linha shebang e um comentário comum.

Uma linha que começa com #!é tanto um comentário quanto qualquer outra linha que começa com #. Isso é verdade se #!for a primeira linha do arquivo ou em qualquer outro lugar. #!/bin/sh tem um efeito , mas não é lido pelo próprio intérprete .

#não é um comentário em todas as linguagens de programação, mas, como você sabe, é um comentário nos shells no estilo Bourne, incluindo she bash(assim como na maioria dos shells no estilo Bourne, como csh). Também é um comentário em Python . E é um comentário em uma variedade de arquivos de configuração que não são realmente scripts (como /etc/fstab).

Suponha que um script de shell comece com #!/bin/sh. Isso é um comentário, e o intérprete (o shell) ignora tudo na linha após o #personagem.

O objetivo de uma #!linha não é fornecer informações ao intérprete. O objetivo da #!linha é informar ao sistema operacional (ou qualquer processo que inicie o intérprete) o que usar como intérprete .

  • Se você chamar o script como um arquivo executável, por exemplo, executando ./script.sh, o sistema consultará a primeira linha para ver se começa com #!, seguido por zero ou mais espaços, seguido por um comando. Se isso acontecer, ele executa esse comando com o nome do script como argumento. Neste exemplo, ele é executado /bin/sh script.sh(ou, tecnicamente /bin/sh ./script.sh).

  • Se você chamar o script chamando explicitamente o intérprete, a #!linha nunca será consultada. Portanto, se você executar sh script.sh, a primeira linha não terá efeito. Se script2.sha primeira linha for #!/usr/games/nibbles, a execução sh script2.shnão tentará abrir o script nibbles(mas ./script2.shserá).

Você notará que em nenhum dos casos a extensão do script ( .sh), se houver, afeta a maneira como é executada. Em um sistema semelhante ao Unix, isso normalmente não afeta a maneira como o script é executado. Em alguns outros sistemas, como o Windows, a #!linha shebang pode ser totalmente ignorada pelo sistema e a extensão pode determinar o que executa os scripts. (Isso não significa que você precise fornecer extensões aos seus scripts, mas é um dos motivos pelos quais, se o fizer, elas devem estar corretas.)

#!foi escolhido para servir a esse propósito precisamente porque # inicia um comentário. A #!linha é para o sistema, não para o intérprete, e deve ser ignorada pelo intérprete.

Linha Shebang para scripts Bash

Você (originalmente) disse que usa #!/bin/shpara bashscripts. Você só deve fazer isso se o script não exigir nenhuma das bashextensões - shprecisa poder executar o script. shnem sempre é um link simbólico para bash. Freqüentemente, inclusive em todos os sistemas Debian e Ubuntu remotamente recentes , shexiste um link simbólico para dash.

Linha Shebang para scripts Python

Você também disse (na primeira versão da sua pergunta, antes de editar) que iniciou seus scripts Python #!/bin/sh read by the interpretor. Se você quer dizer isso literalmente, definitivamente deveria parar de fazer isso. Se hello.pycomeça com essa linha, a execução é ./hello.pyexecutada:

/bin/sh read by the interpretor hello.py

/bin/shtentará executar um script chamado read(com by the interpretor hello.pyseus argumentos), read(espero) não será encontrado, e seu script Python nunca será visto por um intérprete Python.

Se você está cometendo esse erro, mas não está tendo o problema que estou descrevendo, provavelmente está invocando os scripts Python especificando explicitamente o interpretador (por exemplo, python hello.py), fazendo com que a primeira linha seja ignorada. Quando você distribui seus scripts para outras pessoas ou os utiliza muito tempo depois, pode não estar claro se isso é necessário para que eles funcionem. É melhor corrigi-los agora. Ou, pelo menos, remova a primeira linha completamente, para que, quando eles falharem em executar ./a mensagem de erro, faça sentido.

Para scripts Python, se você souber onde o interpretador Python está (ou será), você pode escrever a #!linha da mesma maneira:

#!/usr/bin/python

Ou, se for um script Python 3, você deve especificar python3, já que pythonquase sempre é Python 2 :

#!/usr/bin/python3

No entanto, o problema é que, embora /bin/shsempre deva existir e /bin/bashquase sempre exista nos sistemas que bashacompanham o sistema operacional, o Python pode existir em vários lugares.

Portanto, muitos programadores Python usam isso:

#!/usr/bin/env python

(Ou #!/usr/bin/env python3para Python 3.)

Isso faz com que o script confie em envestar no "lugar certo" em vez de confiar em pythonestar no lugar certo. Isso é bom, porque:

  • envestá quase sempre localizado em /usr/bin.
  • Na maioria dos sistemas, o python que executar seu script é aquele que aparece primeiro na PATH. Começando hello.pycom #!/usr/bin/env pythonmake ./hello.pyrun /usr/bin/env python hello.py, que é (virtualmente) equivalente à execução python hello.py.

A razão pela qual você não pode usar #!pythoné que:

  • Você deseja que o intérprete especificado seja fornecido por um caminho absoluto (ou seja, começando com /).
  • O processo de chamada seria executado python no diretório atual . Procurar o caminho quando o comando não contém uma barra é um comportamento específico do shell.

Ocasionalmente, um Python ou outro script que não seja um shell script terá uma linha shebang começando com #!/bin/sh ...onde ...está outro código. Às vezes, isso é correto, porque existem algumas maneiras de chamar o shell compatível com Bourne ( sh) com argumentos para fazê-lo chamar um intérprete Python. (Um dos argumentos provavelmente conterá python.) No entanto, para a maioria dos propósitos, #!/usr/bin/env pythoné mais simples, elegante e com maior probabilidade de funcionar da maneira que você deseja.

Linhas Shebang em outros idiomas

Muitas linguagens de programação e script e alguns outros formatos de arquivo usam #como comentário. Para qualquer um deles, um arquivo no idioma pode ser executado por um programa que o utiliza como argumento, especificando o programa na primeira linha seguinte #!.

Em algumas linguagens de programação, #normalmente não é um comentário, mas como um caso especial, a primeira linha é ignorada se começar #!. Isso facilita o uso da #!sintaxe, embora #não faça de uma linha um comentário.

Linhas Shebang para arquivos que não são executados como scripts

Embora seja menos intuitivo, qualquer arquivo cujo formato de arquivo possa acomodar uma primeira linha começando com #!seguido pelo caminho completo de um executável pode ter uma linha shebang. Se você fizer isso, e o arquivo estiver marcado como executável, poderá executá-lo como um programa ... fazendo com que seja aberto como um documento.

Alguns aplicativos usam esse comportamento intencionalmente. Por exemplo, no VMware, os .vmxarquivos definem máquinas virtuais. Você pode "executar" uma máquina virtual como se fosse um script, porque esses arquivos são marcados como executáveis ​​e têm uma linha shebang, fazendo com que sejam abertos em um utilitário VMware.

Linhas Shebang para arquivos que não são executados como scripts, mas agem como scripts de qualquer maneira

rmremove arquivos. Não é uma linguagem de script. No entanto, um arquivo que inicia #!/bin/rme está marcado como executável pode ser executado e, quando você o executa, rmé invocado nele, excluindo-o.

Isso geralmente é conceituado como "o arquivo se exclui". Mas o arquivo não está realmente em execução. É mais parecido com a situação descrita acima para .vmxarquivos.

Ainda assim, como a #!linha facilita a execução de um comando simples (incluindo argumentos da linha de comando), você pode executar alguns scripts dessa maneira. Como um exemplo simples de um "script" mais sofisticado do que #!/bin/rm, considere:

#!/usr/bin/env tee -a

Isso leva a entrada do usuário interativamente, faz eco de volta para o usuário linha por linha e a anexa ao final do arquivo "script".

Útil? Não muito. Conceitualmente interessante? Totalmente! Sim. (Um pouco.)

Conceitos de programação / script conceitualmente semelhantes (apenas por diversão)

Eliah Kagan
fonte
@Rinzwind Thx! (Btw esta resposta não se origina em outro lugar, se é isso que você está querendo saber.)
Elias Kagan
@Rinzwind Não se preocupe, com 8 upvotes após 1 hora, é susceptível de levantar muito mais :-)
guntbert
11
Se sempre for ignorado, o que a -xbandeira Pythons faz?
gerrit
4
@gerrit Boa pergunta. Em qualquer idioma em que o intérprete / compilador relate mensagens com números de linhas, o conteúdo dos comentários será ignorado, mas as linhas de comentários ainda serão contadas . A adição de um comentário ou linha em branco antes de uma linha de código ainda resulta nessa linha de código ter seu número de linha incrementado. -x"pula [a] primeira linha ..." a segunda linha é numerada em 1vez de 2, a terceira linha em 2vez de 3etc. É por isso que você não deve usar esse sinalizador. ;) -xé para scripts em sistemas operacionais que não sejam do tipo Unix e cuja sintaxe do tipo shebang não comece #(portanto, não é um comentário do Python).
Eliah Kagan
4
Em Perl, se o intérprete é iniciado directamente ( perl script.plvs ./script.pl), em seguida, o intérprete irá ler a linha de organização para analisar bandeiras tais como -w. Não é recomendável confiar nesse recurso.
Pare de prejudicar Monica
7

Um shebang é a sequência de caracteres que consiste no sinal numérico e no ponto de exclamação dos caracteres (por exemplo, "#!") Quando ocorre como os dois caracteres iniciais na linha inicial de um script.

Nos sistemas operacionais * nix, quando um script começando com um shebang é executado, o carregador do programa analisa o restante da linha inicial do script como uma diretiva de intérprete; o programa de intérprete especificado é executado, passando para ele como argumento o caminho que foi usado inicialmente ao tentar executar o script. Por exemplo, se um script for nomeado com o caminho "path / to / your-script" e começar com a seguinte linha:

#!/bin/sh

o carregador do programa é instruído a executar o programa "/ bin / sh", por exemplo, o shell Bourne ou um shell compatível, passando "caminho / para / seu-script" como o primeiro argumento.

Assim, um script é nomeado com o caminho "path / to / python-script" e começa com a seguinte linha:

#!/bin/python

então o programa carregado é instruído a executar o programa "/ bin / python", por exemplo, interpretador Python, passando "path / to / python-script" como o primeiro argumento.

Resumindo, "#" comentará uma linha enquanto a sequência de caracteres "#!" ocorrer como os dois primeiros caracteres na linha inicial de um script tem o significado descrito acima.

Para detalhes, consulte Por que alguns scripts começam com #! ...?

Fonte: Algumas seções desta resposta são derivadas (com pequenas modificações) de Shebang (Unix) na Wikipedia em inglês (por colaboradores da Wikipedia ). Este artigo está licenciado sob CC-BY-SA 3.0 , o mesmo que o conteúdo do usuário aqui na AU, portanto, essa derivação é permitida com atribuição.

Goran Miskovic
fonte
4

#!é chamado de shebangquando ocorre como os dois caracteres iniciais na linha inicial de um script. É usado em scripts para indicar um intérprete para execução. O shebangé para o sistema operacional (kernel), não para o shell; portanto, não será interpretado como um comentário.

Cortesia: http://en.wikipedia.org/wiki/Shebang_%28Unix%29

Em geral, se um arquivo é executável, mas na verdade não é um programa executável (binário), e essa linha está presente, o programa especificado após #! é iniciado com o scriptname e todos os seus argumentos. Esses dois caracteres # e! tem que ser os dois primeiros bytes no arquivo!

Informações detalhadas: http://wiki.bash-hackers.org/scripting/basics#the_shebang

saji89
fonte
0

Não, é usado apenas pela execchamada do sistema do kernel Linux e tratado como um comentário pelo intérprete

Quando você faz no bash:

./something

no Linux, este chama a execchamada de sistema com o caminho ./something.

Essa linha do kernel é chamada no arquivo passado para exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

Ele lê os primeiros bytes do arquivo e os compara #!.

Se a comparação for verdadeira, o restante da linha será analisado pelo kernel do Linux, que fará outra execchamada com o caminho /usr/bin/env pythone o arquivo atual como o primeiro argumento:

/usr/bin/env python /path/to/script.py

e isso funciona para qualquer linguagem de script que use #como caractere de comentário.

E sim, você pode fazer um loop infinito com:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

O Bash reconhece o erro:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! passa a ser legível por humanos, mas isso não é necessário.

Se o arquivo execfosse iniciado com bytes diferentes, a chamada do sistema usaria um manipulador diferente. O outro manipulador interno mais importante é para arquivos executáveis ​​do ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305, que verifica se há bytes 7f 45 4c 46(que também são humanos legível para .ELF). Vamos confirmar que, lendo os 4 primeiros bytes de /bin/ls, que é um executável ELF:

head -c 4 "$(which ls)" | hd 

resultado:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

Portanto, quando o kernel vê esses bytes, ele pega o arquivo ELF, coloca-o na memória corretamente e inicia um novo processo com ele. Consulte também: https://stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861

Por fim, você pode adicionar seus próprios manipuladores shebang com o binfmt_miscmecanismo. Por exemplo, você pode adicionar um manipulador personalizado para .jararquivos . Esse mecanismo ainda suporta manipuladores por extensão de arquivo. Outro aplicativo é executar de forma transparente executáveis ​​de uma arquitetura diferente com o QEMU .

Eu não acho que o POSIX especifique shebangs, no entanto: https://unix.stackexchange.com/a/346214/32558 , embora mencione nas seções de justificativa e na forma "se scripts executáveis ​​são suportados pelo sistema, algo pode acontecer".

Ciro Santilli adicionou uma nova foto
fonte