Quando um script de shell começa com #! , 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 da shebang, você precisa entender como um programa é executado.
Executar 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 chamada do sistema execve substitui o código do processo atualmente em execução; há uma chamada de sistema separada fork para criar um novo processo.
Para fazer isso, o kernel deve suportar o formato do arquivo executável. Este arquivo deve conter código de máquina, organizado de forma que o kernel entenda. 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 com #! , ele lê os próximos caracteres e interpreta a primeira linha do arquivo (menos o espaço #! e opcional) como um caminho para outro arquivo (mais argumentos, que Eu não vou discutir aqui). Quando o kernel é instruído a 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 . Em seguida, cabe a /some/interpreter decidir que /my/script é um arquivo de script que deve ser executado.
E se um arquivo não contiver código nativo em um formato que o kernel entenda e não inicie com um shebang? Bem, o arquivo não é executável e a chamada do sistema execve falha com o código de erro ENOEXEC (erro de formato executável).
Este pode ser o fim da história, mas a maioria das shells implementam 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 shell script, ele o executa sozinho. Os detalhes de como isso faz isso depende do shell. Você pode ver um pouco do que está acontecendo, adicionando ps $$ em seu script e mais observando o processo com strace -p1234 -f -eprocess , em 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 de código bash original e os argumentos da linha de comando original transmitidos quando você chamou bash originalmente. 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, em contraste, reage a ENOEXEC chamando /bin/sh com o caminho para o script passado como argumento. Em outras palavras, quando você executa um script shebangless de dash, ele se comporta como se o script tivesse uma linha shebang com #!/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