Por que não usar shebangs sem caminho?

Respostas:

20

A pesquisa PATH é um recurso da biblioteca C padrão no espaço do usuário, assim como as variáveis ​​de ambiente em geral. O kernel não vê variáveis ​​de ambiente, exceto quando passa por um ambiente do chamador execvepara o novo processo.

O kernel não executa nenhuma interpretação no caminho execve(depende das funções do wrapper, como execvpexecutar a pesquisa PATH) ou em um shebang (que mais ou menos redireciona a execvechamada internamente). Então você precisa colocar o caminho absoluto no shebang¹. A implementação original do shebang era apenas algumas linhas de código e não foi significativamente expandida desde então.

Nas primeiras versões do Unix, o shell fez o trabalho de se chamar quando percebeu que você estava chamando um script. Shebang foi adicionado ao kernel por várias razões (resumindo a lógica de Dennis Ritchie :

  • O chamador não precisa se preocupar se um programa para executar é um shell script ou um binário nativo.
  • O próprio script especifica qual intérprete usar, em vez do chamador.
  • O kernel usa o nome do script nos logs.

Shebangs sem caminho exigiriam aumentar o kernel para acessar variáveis ​​e processos do ambiente PATH, ou fazer com que o kernel execute um programa no espaço do usuário que execute a pesquisa PATH. O primeiro método requer a adição de uma quantidade desproporcional de complexidade ao kernel. O segundo método já é possível com um #!/usr/bin/envshebang .

¹ Se você colocar um caminho relativo, ele será interpretado em relação ao diretório atual do processo (não ao diretório que contém o script), o que dificilmente é útil em um shebang.

Gilles 'SO- parar de ser mau'
fonte
2
Não, o kernel não requer um caminho absoluto execvenem no shebang, embora faça pouco sentido ter um caminho relativo em um shebang.
Stéphane Chazelas
3
Para quem está curioso sobre como "o shebang redireciona a chamada executve internamente": na verdade, faz parte de um mecanismo genérico para executar intérpretes em executáveis ​​que precisam deles. Os executáveis ​​ELF vinculados dinamicamente são "interpretados" por /lib64/ld-linux-x86-64.so.2(consulte a lddsaída). O Linux o torna totalmente genérico: o binfmtsuporte (desde 2.1.43) permite registrar pares de intérprete / caminho / número mágico ou extensão de arquivo. Você pode ter PE32 .exeinvoke s winequando você executá-los, arquivos de classe e jar Java invocar java, etc. etc.
Peter Cordes
#! / usr / bin / env -S [shebang] foi necessário para executar o nó sem conhecer seu caminho (usando o nvm - que o coloca em um local diferente do que eu esperava inicialmente).
TamusJRoyce 24/02
-S está disponível apenas desde o coreutils 8.30. Consulte gitlab.com/gnuwget/wget/commit/… .
Tim Ruehsen rockdaboot
19

Há mais coisas acontecendo do que aparenta. #!As linhas são interpretadas pelo kernel Unix ou Linux, #!não é um aspecto dos shells. Isso significa que PATHrealmente não existe no momento em que o kernel decide o que executar.

A maneira mais comum de lidar com o desconhecimento de qual executável executar, ou chamar de perlmaneira portátil ou similar, é usar #!/usr/bin/env perl. O kernel é executado /usr/bin/env, que herda uma PATHvariável de ambiente. envachados (neste exemplo) perlem PATHe usa a execve(2)chamada de sistema para obter o kernel para executar o perlexecutável.

Bruce Ediger
fonte
4
$ strace sleep 1
execve("/usr/bin/sleep", ["sleep", "1"], [/* 99 vars */]) = 0

A conversão para o caminho completo é feita pelo shell (mais geral: no espaço do usuário). O kernel espera um nome / caminho de arquivo que possa acessar diretamente.

Se você deseja que o sistema encontre seu executável olhando através da variável PATH, você pode reescrever seu shebang como #!/usr/bin/env EXEC.

Mas também neste caso, não é o kernel que faz a pesquisa.

Hauke ​​Laging
fonte
1
A conversão para o caminho completo é feita pelo shell Obrigado, embora ... o exemplo deva ilustrar isso? A meu ver, o shell está apenas em execução strace(convertido /usr/bin/straceem algum momento) com 2 argumentos.
Alois Mahdal