Por que o comportamento da sintaxe `#!` Não é especificado pelo POSIX?


17

Na página Linguagem de Comando do Shell da especificação POSIX:

Se a primeira linha de um arquivo de comandos shell começar com os caracteres "#!", Os resultados não serão especificados.

Por que o comportamento de #!não especificado pelo POSIX? Acho desconcertante que algo tão portátil e amplamente usado tenha um comportamento não especificado.


1
Os padrões deixam as coisas não especificadas para não vincular as implementações a comportamentos específicos. Por exemplo, um "login" é "A atividade não especificada pela qual um usuário obtém acesso ao sistema".
Kusalananda

2
Como o POSIX não especifica caminhos executáveis, uma linha shebang é inerentemente não-portátil; Não tenho certeza de que muito seria ganho especificando-o independentemente.
Michael Homer

1
@MichaelHomer, com certeza não? O padrão pode especificar que a linha contenha um caminho a ser usado para o intérprete, mesmo sem dizer qual deve ser o caminho.
Ilkkachu

1
@HaroldFischer Exceto que não é interpretado pelo shell, ele é interpretado pelo kernel do SO (feito pelo menos no Linux, que pode realmente desativar esse suporte durante o tempo de construção) ou por qualquer biblioteca que implemente a exec()função. Portanto, checar contra várias conchas realmente não diz como é portátil.
Austin Hemmelgarn

2
@HaroldFischer Além disso, mesmo entre os sistemas operacionais compatíveis com POSIX, o comportamento não é consistente. O Linux e o macOS se comportam de maneira diferente: o Linux não tokeniza totalmente a linha shebang por espaços. O macOS não permite que o interpretador de script seja outro script. Veja também en.wikipedia.org/wiki/Shebang_(Unix)#Portability
jamesdlin

Respostas:


21

Penso principalmente porque:

  • o comportamento varia muito entre a implementação. Consulte https://www.in-ulm.de/~mascheck/various/shebang/ para obter todos os detalhes.

    No entanto, agora ele pode especificar um subconjunto mínimo da maioria das implementações do tipo Unix: como #! *[^ ]+( +[^ ]+)?\n(com apenas caracteres do conjunto de caracteres portátil nomeado nessas uma ou duas palavras) em que a primeira palavra é um caminho absoluto para um executável nativo, a coisa não é muito longo e comportamento não especificado se o executável for setuid / setgid e a implementação definida se o caminho do interpretador ou o caminho do script é passado argv[0]para o intérprete.

  • O POSIX não especifica o caminho dos executáveis ​​de qualquer maneira. Vários sistemas têm utilitários pré-POSIX em /bin/ /usr/bine possuem utilitários POSIX em outro lugar (como no Solaris 10, onde /bin/shhá um shell Bourne e o POSIX /usr/xpg4/bin, o Solaris 11 o substituiu pelo ksh93, que é mais compatível com POSIX, mas a maioria dos outros as ferramentas /binainda são antigas que não são POSIX). Alguns sistemas não são POSIX, mas têm um modo / emulação POSIX. Tudo o que o POSIX exige é que exista um ambiente documentado no qual um sistema se comporte no POSIX.

    Veja Windows + Cygwin, por exemplo. Na verdade, com o Windows + Cygwin, o she-bang é respeitado quando um script é invocado por um aplicativo cygwin, mas não por um aplicativo nativo do Windows.

    Portanto, mesmo que o POSIX especifique o mecanismo shebang, ele não poderá ser usado para escrever scripts POSIX sh/ sed/ awk... (observe também que o mecanismo shebang não pode ser usado para gravar scripts confiáveis sed/ awk, pois não permite a passagem de um final de opção marcador).

Agora, o fato de não ser especificado não significa que você não pode usá-lo (bem, ele diz que você não deve ter a primeira linha #!se você espera que seja apenas um comentário regular e não um golpe de mulher), mas esse POSIX não oferece garantia se você o fizer.

Na minha experiência, o uso de shebangs oferece mais garantia de portabilidade do que a maneira de escrever scripts de shell do POSIX: deixe de lado, escreva o script na shsintaxe do POSIX e espere que o que chama o script, seja compatível com o POSIX sh, o que é Tudo bem se você souber que o script será chamado no ambiente certo pela ferramenta certa, mas não o contrário.

Você pode ter que fazer coisas como:

#! /bin/sh -
if : ^ false; then : fine, POSIX system by default
else
  # cover Solaris 10 or older. ": ^ false" returns false
  # in the Bourne shell as ^ is an alias for | there for
  # compatibility with the Thomson shell.
  PATH=`getconf PATH`:$PATH; export PATH
  exec /usr/xpg4/bin/sh - "$0" ${1+"$@"}
fi
# rest of script

Se você deseja ser portável para Windows + Cygwin, pode ser necessário nomear seu arquivo com uma extensão .batou .ps1e usar algum truque semelhante para cmd.exeou powershell.exeinvocar o cygwin shno mesmo arquivo.


Curiosamente, da edição 5 : "A construção #! É reservada para implementações que desejam fornecer essa extensão. Um aplicativo portátil não pode usar #! Como a primeira linha de um script de shell; pode não ser interpretado como um comentário."
Muru

@muru Se o script fosse verdadeiramente portátil, em um sistema POSIX executando um POSIX sh, ele não precisaria de uma linha hashbang, pois seria executado pelo POSIX sh.
Kusalananda

1
@Kusalananda isso só é verdade se execlpou execvpfoi usado, certo? Se eu fosse usar execve, isso resultaria em ENOEXEC?
Muru

9

[O] comportamento parece consistente entre todos os cartuchos de reclamação POSIX. Não vejo necessidade de espaço de manobra aqui.

Você não está olhando o suficiente.

Na década de 1980, esse mecanismo não era de fato padronizado. Embora Dennis Ritchie a tivesse implementado, essa implementação não havia atingido o público no lado AT&T do universo. Ele era efetivamente disponível apenas publicamente e conhecido no BSD; com scripts de shell executáveis ​​não disponíveis no AT&T Unix. Portanto, não era razoável padronizá-lo. O estado de coisas é exemplificado por este doco contemporâneo, um de muitos:

Observe que o BSD permite que arquivos que começam com #! interpretersejam executados diretamente, enquanto o SysV permite que apenas arquivos a.out sejam executados diretamente. Isso significa que uma instância de uma das exec…()rotinas em um programa BSD pode ter que ser alterada no SysV para executar o intérprete (tipicamente /bin/sh) para esse programa.
- Stephen Frede (1988). "Programando no System X Release Y". Boletim informativo do grupo de usuários do Australian Unix Systems . Volume 9. Número 4. p. 111

Um ponto importante aqui é que você está observando shells, enquanto a existência de scripts de shell executáveis ​​é realmente uma questão de exec…()função. O que os shells fazem inclui os precursores do mecanismo de script executável, ainda hoje encontrado em alguns shells (e também hoje em dia é obrigatório para o exec…p()subconjunto de funções), e é um tanto enganador. O que o padrão precisa abordar a esse respeito é como, exec…()em um script interpretado, funciona e, no momento em que o POSIX foi criado, ele simplesmente não funcionava em primeiro lugar em grande parte do espectro dos sistemas operacionais de destino .

Uma pergunta subordinado é por que isso não foi padronizado, já que, sobretudo porque o mecanismo número mágico para intérpretes de script tinha alcançado o público no lado AT & T do universo e foi documentado para exec…()na Definição do Sistema 5 de interface , na virada da década de 1990 :

Um arquivo de intérprete começa com uma linha do formulário

#! nome do caminho [arg]
em que caminho é o caminho do intérprete e arg é um argumento opcional. Quando você execcria um arquivo de intérprete, o sistema execé o intérprete especificado.
- exec. System V Interface Definition . Volume 1. 1991.

Infelizmente, o comportamento permanece hoje quase tão divergente quanto na década de 1980 e não existe um comportamento verdadeiramente comum para padronizar. Alguns Unices (famosos HP-UX e FreeBSD, por exemplo) não suportam scripts como intérpretes. Se a primeira linha é um, dois ou muitos elementos separados por espaço em branco varia entre o MacOS (e versões do FreeBSD antes de 2005) e outros. O comprimento máximo do caminho suportado varia. e os caracteres fora do conjunto de caracteres de nome de arquivo portátil POSIX são complicados, assim como os espaços em branco à esquerda e à direita. O argumento do 0º, 1º e 2º também é complicado, com variação significativa entre os sistemas. Alguns atualmente em conformidade com POSIX, mas nãoOs sistemas -Unix ainda não suportam nenhum mecanismo desse tipo e exigir que os convertesse para não serem mais compatíveis com POSIX.

Leitura adicional


1

Conforme observado por algumas das outras respostas, as implementações variam. Isso torna difícil padronizar e preservar a compatibilidade com os scripts existentes. Isso é verdade mesmo para os sistemas POSIX modernos. Por exemplo, o Linux não tokeniza totalmente a linha shebang por espaços. O macOS não permite que o interpretador de script seja outro script.

Veja também http://en.wikipedia.org/wiki/Shebang_(Unix)#Portability

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.