web-dev-qa-db-fra.com

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

Dans bash ou sh, je suppose que tout ce qui commence par # est un commentaire.

Mais dans les scripts bash, nous écrivons:

#!/bin/bash

Et dans Python scripts, il y a:

#!/bin/python

Cela signifie-t-il que # est en soi un commentaire alors que #! ne l’est pas?

65
Gaurav Sharma

La ligne #! est utilisée avant le script est exécuté, puis ignorée lorsque le script est exécuté.

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

Une ligne commençant par #! est autant un commentaire qu'une autre ligne commençant par #. Cela est vrai si le #! est 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 .

# n'est pas un commentaire dans tous les langages de programmation, mais, comme vous le savez, il s'agit d'un commentaire dans les shells de style Bourne, y compris sh et bash (ainsi que la plupart des shells de style non Bourne, comme 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. Il s’agit d’un commentaire et l’interprète (le shell) ignore tout ce qui se trouve sur la ligne après le caractère #.

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

  • Si vous appelez le script en tant que fichier exécutable, par exemple en 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 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. Ainsi, si vous exécutez sh script.sh, la première ligne n'a aucun effet. Si la première ligne de script2.sh est #!/usr/games/nibbles, l'exécution de sh script2.sh n'essaiera pas d'ouvrir le script dans nibbles (mais ./script2.sh sera utilisée).

Vous remarquerez que dans aucun cas l'extension du script (.sh), si elle en a un, n'a une incidence sur la façon dont il est exécuté. 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 donner à vos scripts des extensions, mais c’est l’une des raisons pour lesquelles elles devraient être correctes.)

#! a été choisi pour répondre précisément à cet objectif car # commence un commentaire. La ligne #! concerne le système, pas l'interpréteur, et il devrait être ignoré par l'interpréteur.

Shebang Line pour les scripts Bash

Vous avez dit (à l'origine) que vous utilisiez #!/bin/sh pour les scripts bash. Vous ne devriez le faire que si le script ne nécessite aucune des extensions de bash --sh doit pouvoir exécuter le script. sh n'est pas toujours un lien symbolique vers bash. Souvent, y compris sur tous les systèmes Debian et Ubuntu à distance récents , sh est un lien symbolique vers dash.

Ligne Shebang pour Python Scripts

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

/bin/sh read by the interpretor hello.py

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

Si vous faites cette erreur mais que vous ne rencontrez pas le problème que je décris, vous appelez probablement votre script 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 à exécuter avec ./, le message d’erreur a 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 python est presque toujours Python 2 :

#!/usr/bin/python3

Cependant, le problème est que, alors que /bin/sh est censé exister toujours et que /bin/bash existe presque toujours sur les systèmes où bash est fourni avec le système d’exploitation, Python peut exister à divers endroits.

Par conséquent, de nombreux programmeurs Python utilisent plutôt ceci:

#!/usr/bin/env python

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

Cela fait que le script s'appuie sur env pour être au "bon endroit" au lieu de s'appuyer sur python pour être au bon endroit. C'est une bonne chose car:

  • env est presque toujours situé dans /usr/bin.
  • Sur la plupart des systèmes, quel que soit python qui doit exécuter votre script est celui qui apparaît en premier dans PATH. En démarrant hello.py avec #!/usr/bin/env python, exécutez ./hello.py et exécutez /usr/bin/env python hello.py, ce qui revient (virtuellement) à exécuter python hello.py.

La raison pour laquelle vous ne pouvez pas utiliser #!python est 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 exécuterait pythondans 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 Python ou un autre script qui n'est pas un script Shell aura une ligne Shebang commençant par #!/bin/sh ..., où ... est un autre code. Ceci est parfois correct, car il existe des moyens d'appeler le shell compatible Bourne (sh) avec des arguments pour le faire appeler un interpréteur Python. (L'un des arguments contiendra probablement python.) Cependant, dans la plupart des cas, #!/usr/bin/env python est 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 # en tant que commentaire. 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 après le #!.

Dans certains langages de programmation, # n'est normalement pas un commentaire, mais dans un cas spécial, la première ligne est ignorée si elle commence par #!. Cela facilite l'utilisation de la syntaxe #! même si # ne fait pas autrement d'une ligne 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 fichiers .vmx 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

rm supprime les fichiers. Ce n'est pas un langage de script. Toutefois, un fichier qui commence #!/bin/rm et est marqué comme exécutable peut être exécuté. Lorsque vous l'exécutez, rm est 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 fichiers .vmx.

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 script et de conceptions similaires (juste pour le plaisir)

99
Eliah Kagan

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 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 programme de chargement reçoit pour instruction d'exécuter le programme "/ bin/sh" à la place, par exemple. Bourne Shell ou un shell compatible, en passant "chemin/vers/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

puis le programme chargé est invité à exécuter le programme "/ bin/python" à la place, par exemple. Python interprète, 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 sont dérivées (avec de légères modifications) de Shebang (Unix) sur anglais Wikipedia (par les contributeurs Wikipedia ). Cet article est sous licence CC-BY-SA 3. , identique au contenu utilisateur affiché ici sur AU, cette dérivation est donc autorisée avec attribution.

7
Goran Miskovic

#! est appelé Shebang quand il apparaît sous la forme des deux caractères initiaux de la première ligne d'un script. Il est utilisé dans les scripts pour indiquer un interprète à exécuter. Shebang est 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

4
saji89

Non, il n'est utilisé que par l'appel système exec du noyau Linux et traité comme un commentaire par l'interpréteur

Quand vous faites sur bash:

./something

sous Linux, cela appelle l'appel système exec 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

si ((bprm-> buf [0]! = '#') || (bprm-> buf [1]! = '!'))

Ceci lit les tout premiers octets du fichier et les compare à #!.

Si cela est vrai, alors le reste de la ligne est analysé par le noyau Linux, qui effectue un autre appel exec avec le chemin /usr/bin/env python et le fichier en cours comme premier argument:

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

et cela fonctionne pour tout langage de script qui utilise # en tant que 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 être lisible par l'homme, mais ce n'est pas obligatoire.

Si le fichier a démarré avec des octets différents, l'appel système exec utilise 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 recherche des octets. 7f 45 4c 46 (qui est également lisible par .ELF). Cela lit 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 mécanisme binfmt_misc. Par exemple, vous pouvez ajouter un gestionnaire personnalisé pour les fichiers .jar . Ce mécanisme prend même en charge les gestionnaires par extension de fichier. Une autre application consiste à exécute de manière transparente des exécutables d’une architecture différente avec QEM .

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 des scripts exécutables sont pris en charge par le système, quelque chose peut arriver ".