Le site exec
du noyau Linux comprend les shebangs ( #!
) nativement
Quand vous faites sur bash :
./something
sous Linux, cela appelle le exec
appel système avec le chemin ./something
.
Cette ligne du noyau est appelée sur le fichier passé à exec
: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
Il lit les tout premiers octets du fichier, et les compare à #!
.
Si la comparaison est vraie, alors le reste de la ligne est analysé par le noyau Linux, qui fait un autre exec
appel avec chemin /usr/bin/python3
et le fichier courant comme premier argument :
/usr/bin/python3 /path/to/script.py
et cela fonctionne pour tout langage de script qui utilise #
comme caractère de commentaire.
Et par analogie, si vous décidez d'utiliser env
à la place, ce que vous devriez probablement toujours faire pour travailler sur des systèmes qui ont le format python3
dans un autre endroit, notamment pyenv
voir aussi cette question tout le tralala :
#!/usr/bin/env python3
finit par appeler de manière analogue :
/usr/bin/env python3 /path/to/script.py
qui fait ce que vous attendez de env python3
: recherches PATH
para python3
et fonctionne /usr/bin/python3 /path/to/script.py
.
Et oui, vous pouvez faire une boucle infinie avec :
printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a
Bash reconnaît l'erreur :
-bash: /a: /a: bad interpreter: Too many levels of symbolic links
#!
se trouve être lisible par l'homme, mais ce n'est pas obligatoire.
Si le fichier a commencé avec des octets différents, alors l'option exec
utiliserait un gestionnaire différent. L'autre gestionnaire intégré le plus important concerne les fichiers exécutables ELF : https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 qui vérifie la présence d'octets 7f 45 4c 46
(qui se trouve également être lisible par l'homme pour le .ELF
). Confirmons cela en lisant les 4 premiers octets de /bin/ls
qui est un exécutable ELF :
head -c 4 "$(which ls)" | hd
sortie :
00000000 7f 45 4c 46 |.ELF|
00000004
Ainsi, lorsque le noyau voit ces octets, il prend le fichier ELF, le place correctement en mémoire et lance un nouveau processus avec lui. Voir aussi : Comment un noyau fait-il fonctionner un fichier binaire exécutable sous linux ?
Enfin, vous pouvez ajouter vos propres gestionnaires shebang à l'aide de l'option binfmt_misc
mécanisme. Par exemple, vous pouvez ajouter un gestionnaire personnalisé pour .jar
fichiers . Ce mécanisme prend même en charge les gestionnaires par extension de fichier. Une autre application consiste à exécuter de manière transparente des exécutables d'une architecture différente avec QEMU .
Je ne pense pas que POSIX précise cependant les shebangs : https://unix.stackexchange.com/a/346214/32558 Bien qu'il en soit fait mention dans des sections de justification, et sous la forme "si les scripts exécutables sont supportés par le système, quelque chose peut se produire". macOS et FreeBSD semblent également l'implémenter.
PATH
motivation de recherche
Il est probable que l'une des principales raisons de l'existence des shebangs est le fait que sous Linux, nous voulons souvent exécuter des commandes à partir de PATH
tout comme :
basename-of-command
au lieu de :
/full/path/to/basename-of-command
Mais alors, sans le mécanisme shebang, comment Linux saurait-il comment lancer chaque type de fichier ?
Codage en dur de l'extension dans les commandes :
basename-of-command.py
ou d'implémenter la recherche PATH sur chaque interpréteur :
python3 basename-of-command
serait une possibilité, mais cela présente le problème majeur que tout se casse la figure si nous décidons un jour de refactoriser la commande dans un autre langage.
Les shebangs résolvent ce problème à merveille.
Voir aussi : Pourquoi les gens écrivent-ils #!/usr/bin/env python sur la première ligne d'un script Python ?