Lorsqu'un script shell commence par #!
, cette première ligne est un commentaire en ce qui concerne le shell. Cependant, les deux premiers caractères sont significatifs pour une autre partie du système: le noyau. Les deux personnages #!
s'appellent un shebang . Pour comprendre le rôle du shebang, vous devez comprendre comment un programme est exécuté.
L'exécution d'un programme à partir d'un fichier nécessite une action du noyau. Cela se fait dans le cadre de l' execve
appel système. Le noyau doit vérifier les autorisations de fichier, libérer les ressources (mémoire, etc.) associées au fichier exécutable en cours d'exécution dans le processus d'appel, allouer des ressources pour le nouveau fichier exécutable et transférer le contrôle au nouveau programme (et plus de choses qui Je ne mentionnerai pas). L' execve
appel système remplace le code du processus en cours d'exécution; il existe un appel système distinct fork
pour créer un nouveau processus.
Pour ce faire, le noyau doit prendre en charge le format du fichier exécutable. Ce fichier doit contenir du code machine, organisé d'une manière que le noyau comprend. Un script shell ne contient pas de code machine, il ne peut donc pas être exécuté de cette façon.
Le mécanisme shebang permet au noyau de reporter la tâche d'interprétation du code à un autre programme. Lorsque le noyau voit que le fichier exécutable commence par#!
, il lit les quelques caractères suivants et interprète la première ligne du fichier (moins l' #!
espace de tête et facultatif) comme un chemin vers un autre fichier (plus les arguments, dont je ne parlerai pas ici ). Lorsque le noyau est invité à exécuter le fichier /my/script
et qu'il voit que le fichier commence par la ligne #!/some/interpreter
, le noyau s'exécute /some/interpreter
avec l'argument /my/script
. C'est ensuite /some/interpreter
à décider qu'il /my/script
s'agit d'un fichier de script qu'il doit exécuter.
Que se passe-t-il si un fichier ne contient pas de code natif dans un format que le noyau comprend et ne commence pas par un shebang? Eh bien, le fichier n'est pas exécutable et l' execve
appel système échoue avec le code d'erreur ENOEXEC
(erreur de format exécutable).
Cela pourrait être la fin de l'histoire, mais la plupart des shells implémentent une fonction de secours. Si le noyau revient ENOEXEC
, le shell regarde le contenu du fichier et vérifie s'il ressemble à un script shell. Si le shell pense que le fichier ressemble à un script shell, il l'exécute lui-même. Les détails de la façon dont cela fonctionne dépendent du shell. Vous pouvez voir ce qui se passe en ajoutant ps $$
dans votre script, et plus en observant le processus avec strace -p1234 -f -eprocess
où 1234 est le PID du shell.
En bash, ce mécanisme de secours est implémenté en appelant fork
mais pas execve
. Le processus bash enfant efface lui-même son état interne et ouvre le nouveau fichier de script pour l'exécuter. Par conséquent, le processus qui exécute le script utilise toujours l'image de code bash d'origine et les arguments de ligne de commande d'origine passés lorsque vous avez appelé bash à l'origine. ATT ksh se comporte de la même manière.
% bash --norc
bash-4.3$ ./foo.sh
PID TTY STAT TIME COMMAND
21913 pts/2 S+ 0:00 bash --norc
Dash, en revanche, réagit ENOEXEC
en appelant /bin/sh
avec le chemin d'accès au script passé en argument. En d'autres termes, lorsque vous exécutez un script sans shebang à partir du tableau de bord, cela se comporte comme si le script avait une ligne de shebang avec #!/bin/sh
. Mksh et zsh se comportent de la même manière.
% dash
$ ./foo.sh
PID TTY STAT TIME COMMAND
21427 pts/2 S+ 0:00 /bin/sh ./foo.sh