Est-ce que #! / Bin / sh est lu par l'interprète?

66

En bashou sh, je suppose que tout ce qui commence par #est un commentaire .

Mais dans les bashscripts, nous écrivons:

#!/bin/bash

Et dans les scripts Python, il y a:

#!/bin/python

Est-ce que cela signifie que #par lui-même est un commentaire alors que #!n'est pas?

Gaurav Sharma
la source
1
Et une fois que vous aurez commencé à consulter les profils Apparmor, vous verrez #include. Là aussi, le #n'est pas censé être un commentaire.
4
@ vasa1 Mais le point clé qui est souvent apprécié au sujet des lignes de Hashbang au début des scripts shell est que ils sont des commentaires .
Eliah Kagan

Réponses:

100

La #!ligne est utilisée avant l'exécution du script, puis ignorée lors de son exécution.

Vous demandez quelle est la différence entre une ligne shebang et un commentaire ordinaire.

Une ligne commençant par #!est tout autant un commentaire qu'une autre ligne commençant par #. Cela est vrai s'il #!s'agit de la première ligne du fichier ou ailleurs. #!/bin/sh a un effet , mais il n'est pas lu par l'interprète lui-même .

#Ce n'est pas un commentaire dans tous les langages de programmation, mais, comme vous le savez, c'est un commentaire dans les shells de style Bourne, notamment ( shet) bash(ainsi que la plupart des shells de style non Bourne, par exemple csh). C'est aussi un commentaire en Python . Et c'est un commentaire dans une variété de fichiers de configuration qui ne sont pas vraiment des scripts (comme /etc/fstab).

Supposons qu'un script shell commence par #!/bin/sh. C'est un commentaire, et l'interprète (le shell) ignore tout ce qui se trouve sur la ligne après le #personnage.

Le but d'une #!ligne n'est pas de donner des informations à l'interprète. Le but de la #!ligne est d'indiquer au système d'exploitation (ou quel que soit le processus qui lance l'interpréteur) ce qu'il doit utiliser comme interprète .

  • Si vous appelez le script en tant que fichier exécutable, par exemple en l'exécutant ./script.sh, le système consulte la première ligne pour savoir si commence par #!, suivi de zéro ou plus d'espaces, suivi d'une commande. Si tel est le cas, il exécute cette commande avec le nom du script comme argument. Dans cet exemple, il s'exécute /bin/sh script.sh(ou techniquement /bin/sh ./script.sh).

  • Si vous appelez le script en appelant explicitement l'interpréteur, la #!ligne n'est jamais consultée. Donc, si vous courez sh script.sh, la première ligne n'a aucun effet. Si script2.shla première ligne est #!/usr/games/nibbleslue, l'exécution sh script2.shne tentera pas d'ouvrir le script nibbles(mais le ./script2.shfera).

Vous remarquerez que dans aucun cas l'extension ( .sh) du script , si elle en a une, a une incidence sur son exécution. Dans un système de type Unix, cela n’affecte généralement pas la façon dont le script est exécuté. Sur d'autres systèmes, tels que Windows, la #!ligne shebang peut être entièrement ignorée par le système et l'extension peut déterminer le type d'exécution des scripts. (Cela ne signifie pas que vous devez attribuer des extensions à vos scripts, mais c’est l’une des raisons pour lesquelles elles doivent être correctes.)

#!a été choisi pour servir cet objectif précisément parce que # commence un commentaire. La #!ligne est destinée au système, pas à l'interprète, et l'interprète devrait l'ignorer.

Shebang Line pour les scripts Bash

Vous avez (à l'origine) dit que vous utilisiez #!/bin/shdes bashscripts. Vous ne devriez le faire que si le script ne nécessite aucune bashextension - il shdoit pouvoir exécuter le script. shn'est pas toujours un lien symbolique vers bash. Souvent, y compris sur tous les systèmes Debian et Ubuntu récents et à distance , shun lien symbolique est créé dash.

Ligne Shebang pour les scripts Python

Vous avez également indiqué (dans la première version de votre question, avant la modification) que vous démarrez vos scripts Python #!/bin/sh read by the interpretor. Si vous voulez dire cela littéralement, alors vous devriez absolument arrêter de le faire. Si hello.pycommence par cette ligne, l'exécution ./hello.pys'exécute:

/bin/sh read by the interpretor hello.py

/bin/shessaiera d'exécuter un script appelé read(avec by the interpretor hello.pycomme arguments), readne sera (espérons-le) pas trouvé et votre script Python ne sera jamais vu par un interprète Python.

Si vous faites cette erreur mais n'avez pas le problème que je décris, vous invoquez probablement vos scripts Python en spécifiant explicitement l'interpréteur (par exemple, python hello.py), ce qui a pour effet d'ignorer la première ligne. Lorsque vous distribuez vos scripts à d'autres personnes ou les utilisez longtemps après, il n'est peut-être pas évident que cela leur soit nécessaire. Il est préférable de les réparer maintenant. Ou du moins, supprimez entièrement la première ligne, de sorte que, s’ils ne parviennent pas à s’exécuter avec ./le message d’erreur, cela aura un sens.

Pour les scripts Python, si vous savez où se trouve (ou sera) l'interpréteur Python, vous pouvez écrire la #!ligne de la même manière:

#!/usr/bin/python

Ou, s'il s'agit d'un script Python 3, vous devez spécifier python3, car pythonc'est presque toujours Python 2 :

#!/usr/bin/python3

Cependant, le problème est que, même s'il /bin/shest censé exister toujours et /bin/bashpresque toujours sur les systèmes bashlivrés avec le système d'exploitation, Python peut exister à divers endroits.

Par conséquent, de nombreux programmeurs Python utilisent ceci à la place:

#!/usr/bin/env python

(Ou #!/usr/bin/env python3pour Python 3.)

Cela fait que le script se base sur le fait d' envêtre au "bon endroit" au lieu de se fier pythonau bon endroit. C'est une bonne chose car:

  • envest presque toujours situé dans /usr/bin.
  • Sur la plupart des systèmes, celui qui python doit exécuter votre script est celui qui apparaît en premier dans le répertoire PATH. En commençant hello.pypar #!/usr/bin/env pythonmake ./hello.pyrun /usr/bin/env python hello.py, ce qui est (pratiquement) équivalent à courir python hello.py.

La raison pour laquelle vous ne pouvez pas utiliser #!pythonest que:

  • Vous voulez que l'interprète spécifié soit donné par un chemin absolu (c'est-à-dire en commençant par /).
  • Le processus appelant serait exécuté python dans le répertoire en cours . La recherche du chemin lorsque la commande ne contient pas de barre oblique est un comportement spécifique du shell.

Parfois, un script Python ou autre script qui n'est pas un script shell aura une ligne shebang commençant par #!/bin/sh ...où se ...trouve un autre code. C'est parfois correct, car il existe des moyens d'appeler le shell ( sh) compatible Bourne avec des arguments pour le faire appeler un interpréteur Python. (Un des arguments le contiendra probablement python.) Cependant, dans la plupart des cas, il #!/usr/bin/env pythonest plus simple, plus élégant et plus susceptible de fonctionner comme vous le souhaitez.

Lignes Shebang dans d'autres langues

De nombreux langages de programmation et de script, ainsi que d'autres formats de fichiers, utilisent #comme commentaires. Pour n'importe lequel d'entre eux, un fichier dans la langue peut être exécuté par un programme qui le prend comme argument en spécifiant le programme sur la première ligne suivante #!.

Dans certains langages de programmation, ce #n’est normalement pas un commentaire, mais dans un cas particulier, la première ligne est ignorée si elle commence par #!. Cela facilite l'utilisation de la #!syntaxe même si, par #ailleurs, une ligne n'est pas un commentaire.

Lignes Shebang pour les fichiers qui ne s'exécutent pas en tant que scripts

Bien que ce soit moins intuitif, tout fichier dont le format de fichier peut accueillir une première ligne commençant par #!suivi du chemin complet d'un exécutable peut avoir une ligne shebang. Si vous faites cela, et que le fichier est marqué comme exécutable, vous pouvez l'exécuter comme un programme ... et ainsi l'ouvrir comme un document.

Certaines applications utilisent ce comportement intentionnellement. Par exemple, dans VMware, les .vmxfichiers définissent des machines virtuelles. Vous pouvez "exécuter" une machine virtuelle comme s'il s'agissait d'un script, car ces fichiers sont marqués comme étant exécutables et comportent une ligne shebang, ce qui les ouvre dans un utilitaire VMware.

Lignes Shebang pour les fichiers qui ne s'exécutent pas comme des scripts mais agissent comme des scripts de toute façon

rmsupprime les fichiers. Ce n'est pas un langage de script. Cependant, un fichier qui commence #!/bin/rmet qui est marqué comme exécutable peut être exécuté. Lorsque vous l'exécutez, il rmest appelé, ce qui le supprime.

Ceci est souvent conceptualisé comme "le fichier se supprime lui-même". Mais le fichier ne fonctionne pas vraiment du tout. Cela ressemble plus à la situation décrite ci-dessus pour les .vmxfichiers.

Néanmoins, comme la #!ligne facilite l'exécution d'une commande simple (y compris les arguments de ligne de commande), vous pouvez effectuer certains scripts de cette manière. Comme exemple simple d'un "script" plus sophistiqué que #!/bin/rm, considérons:

#!/usr/bin/env tee -a

Cela prend les entrées de l'utilisateur de manière interactive, les renvoie ligne par ligne à l'utilisateur et les ajoute à la fin du fichier "script".

Utile? Pas très. Conceptuellement intéressant? Totalement! Oui. (Quelque peu.)

Concepts de programmation / script similaires (juste pour le plaisir)

Eliah Kagan
la source
@Rinzwind Thx! (Btw cette réponse ne provient pas ailleurs, si c'est ce que vous vous demandez.)
Eliah Kagan
@Rinzwind Ne vous inquiétez pas, avec 8 votes positifs en 1 heure, il est susceptible de lever beaucoup plus loin :-)
guntbert
1
Si elle est toujours ignorée, que fait l' -xindicateur Pythons ?
gerrit
4
@ gerrit Bonne question. Dans toutes les langues où intepreter / compiler rapporte des messages avec des numéros de ligne, le contenu des commentaires est ignoré, mais les lignes de commentaire sont toujours comptées . L'ajout d'un commentaire ou d'une ligne vide avant une ligne de code entraîne toujours l'incrémentation de cette ligne de code. -x"ignorer la première ligne ..." la 2ème ligne est numérotée 1au lieu de 2, la 3ème ligne 2au lieu de 3, etc. C'est pourquoi vous ne devriez pas utiliser ce drapeau. ;) -xest destiné aux scripts sur des systèmes d’exploitation non-Unix-semblables à la syntaxe shebang-like ne commençant pas par #(donc pas un commentaire de Python).
Eliah Kagan
4
En Perl, si l'interprète est démarré directement ( par perl script.plrapport ./script.pl) , puis l'interprète va lire la ligne de tralala pour analyser des drapeaux tels que -w. Il n'est toutefois pas recommandé de s'appuyer sur cette fonctionnalité.
Cessez de nuire à Monica le
7

Un shebang est la séquence de caractères constituée du signe de nombre de caractères et du point d'exclamation (par exemple, "#!") Lorsqu'il apparaît comme les deux premiers caractères de la première ligne d'un script.

Sous les systèmes d'exploitation * nix, lorsqu'un script commençant par un shebang est exécuté, le programme de chargement analyse le reste de la ligne initiale du script sous forme de directive interpréteur; le programme interpréteur spécifié est exécuté à la place, en lui transmettant en tant qu'argument le chemin initialement utilisé lors de la tentative d'exécution du script. Par exemple, si un script est nommé avec le chemin "chemin / vers / votre script", il commence par la ligne suivante:

#!/bin/sh

ensuite, le chargeur de programmes doit exécuter le programme "/ bin / sh" à la place, par exemple, le shell Bourne ou un shell compatible, en transmettant "path / to / votre-script" comme premier argument.

En conséquence, un script est nommé avec le chemin "chemin / vers / python-script" et commence par la ligne suivante:

#!/bin/python

ensuite, le programme chargé est invité à exécuter le programme "/ bin / python" à la place, par exemple, d'un interpréteur Python, en passant "chemin / vers / python-script" comme premier argument.

En bref, "#" commentera une ligne tandis que la séquence de caractères "#!" apparaissant comme les deux premiers caractères de la première ligne d’un script a la signification décrite ci-dessus.

Pour plus de détails, voir Pourquoi certains scripts commencent par #! ...?

Source: Certaines sections de cette réponse proviennent (avec de légères modifications) de Shebang (Unix) sur Wikipedia anglais (par des contributeurs de Wikipédia ). Cet article est sous licence CC-BY-SA 3.0 , comme le contenu de l'utilisateur, ici sur AU. Cette dérivation est donc autorisée avec attribution.

Goran Miskovic
la source
4

#!est appelé le shebangquand il apparaît comme les deux premiers caractères de la première ligne d'un script. Il est utilisé dans les scripts pour indiquer un interpréteur à exécuter. Le shebangest pour le système d'exploitation (noyau), pas pour le shell; donc cela ne sera pas interprété comme un commentaire.

Courtesy: http://en.wikipedia.org/wiki/Shebang_%28Unix%29

En général, si un fichier est exécutable, mais en réalité pas un programme exécutable (binaire), et qu'une telle ligne est présente, le programme spécifié après #! est lancé avec le nom de script et tous ses arguments. Ces deux personnages # et! doivent être les deux premiers octets du fichier!

Informations détaillées: http://wiki.bash-hackers.org/scripting/basics#the_shebang

saji89
la source
0

Non, il est uniquement utilisé par l' execappel système du noyau Linux et traité comme un commentaire par l'interpréteur.

Quand vous faites sur bash:

./something

sous Linux, cela appelle l' execappel système avec le chemin ./something.

Cette ligne du noyau est appelée sur le fichier transmis à 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 effectue un autre execappel avec le chemin /usr/bin/env pythonet le fichier en cours comme premier argument:

/usr/bin/env python /path/to/script.py

et cela fonctionne pour tout langage de script utilisant #un caractère de commentaire.

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 simplement lisible par l’homme, mais ce n’est pas nécessaire.

Si le fichier a démarré avec différents octets, l' execappel système utilisera un autre gestionnaire. 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 humain). lisible pour .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 met correctement en mémoire et lance un nouveau processus avec celui-ci. Voir aussi: https://stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861

Enfin, vous pouvez ajouter vos propres gestionnaires Shebang avec le binfmt_miscmécanisme. Par exemple, vous pouvez ajouter un gestionnaire personnalisé pour les .jarfichiers . 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 .

Cependant , je ne pense pas que POSIX spécifie des shebangs: https://unix.stackexchange.com/a/346214/32558 , bien qu'il soit mentionné dans les sections de justification, et sous la forme "si les scripts exécutables sont pris en charge par le système se produire".

Ciro Santilli 改造 心心
la source