Quando um script de shell começa #!
, essa primeira linha é um comentário no que diz respeito ao shell. No entanto, os dois primeiros caracteres são significativos para outra parte do sistema: o kernel. Os dois caracteres #!
são chamados de shebang . Para entender o papel do shebang, você precisa entender como um programa é executado.
A execução de um programa a partir de um arquivo requer ação do kernel. Isso é feito como parte da execve
chamada do sistema. O kernel precisa verificar as permissões do arquivo, liberar os recursos (memória, etc.) associados ao arquivo executável atualmente em execução no processo de chamada, alocar recursos para o novo arquivo executável e transferir o controle para o novo programa (e mais coisas que Eu não vou mencionar). A execve
chamada do sistema substitui o código do processo em execução no momento; há uma chamada de sistema separada fork
para criar um novo processo.
Para fazer isso, o kernel precisa suportar o formato do arquivo executável. Este arquivo deve conter o código da máquina, organizado da maneira que o kernel entende. Um script de shell não contém código de máquina, portanto, não pode ser executado dessa maneira.
O mecanismo shebang permite que o kernel adie a tarefa de interpretar o código para outro programa. Quando o kernel vê que o arquivo executável começa #!
, ele lê os próximos caracteres e interpreta a primeira linha do arquivo (menos o #!
espaço inicial e opcional) como um caminho para outro arquivo (mais argumentos, que não serão discutidos aqui) ) Quando o kernel é dito para executar o arquivo /my/script
, e ele vê que o arquivo começa com a linha #!/some/interpreter
, o kernel executa /some/interpreter
com o argumento /my/script
. Cabe então /some/interpreter
decidir que /my/script
esse arquivo de script deve ser executado.
E se um arquivo não contiver código nativo em um formato que o kernel compreenda e não iniciar com um shebang? Bem, o arquivo não é executável e a execve
chamada do sistema falha com o código de erro ENOEXEC
(erro de formato executável).
Este pode ser o fim da história, mas a maioria dos shells implementa um recurso de fallback. Se o kernel retornar ENOEXEC
, o shell examinará o conteúdo do arquivo e verificará se ele se parece com um script de shell. Se o shell achar que o arquivo se parece com um script de shell, ele será executado sozinho. Os detalhes de como isso acontece depende do shell. Você pode ver um pouco do que está acontecendo adicionando ps $$
no seu script e muito mais assistindo ao processo em strace -p1234 -f -eprocess
que 1234 é o PID do shell.
No bash, esse mecanismo de fallback é implementado chamando fork
mas não execve
. O processo bash filho limpa seu estado interno sozinho e abre o novo arquivo de script para executá-lo. Portanto, o processo que executa o script ainda está usando a imagem do código do bash original e os argumentos da linha de comando originais passados quando você chamou o bash originalmente. O ATT ksh se comporta da mesma maneira.
% bash --norc
bash-4.3$ ./foo.sh
PID TTY STAT TIME COMMAND
21913 pts/2 S+ 0:00 bash --norc
Dash, ao contrário, reage ENOEXEC
chamando /bin/sh
com o caminho para o script passado como argumento. Em outras palavras, quando você executa um script sem barra a partir do traço, ele se comporta como se o script tivesse uma linha shebang #!/bin/sh
. Mksh e zsh se comportam da mesma maneira.
% dash
$ ./foo.sh
PID TTY STAT TIME COMMAND
21427 pts/2 S+ 0:00 /bin/sh ./foo.sh