web-dev-qa-db-fra.com

Pourquoi les gens écrivent-ils le Shebang #! / Usr / bin / env python sur la première ligne d'un script Python?

Il me semble que les fichiers fonctionnent de la même manière sans cette ligne.

953
john garcias

Si vous avez plusieurs versions de Python installées, /usr/bin/env s'assurera que l'interpréteur utilisé est la première sur $PATH de votre environnement. L'alternative serait de coder en dur quelque chose comme #!/usr/bin/python; ça va, mais moins flexible.

Sous Unix, un fichier exécutable destiné à être interprété peut indiquer quel interpréteur utiliser en disposant d'un #! au début de la première ligne, suivi de l'interpréteur (et de tout indicateur avoir besoin).

Si vous parlez d'autres plates-formes, bien sûr, cette règle ne s'applique pas (mais cette "ligne Shebang" ne nuit pas, et vous aidera si vous copiez ce script sur une plate-forme avec un Unix base, comme Linux, Mac, etc.).

1016
Alex Martelli

C'est ce qu'on appelle la ligne Shebang . Comme le l'entrée de Wikipedia explique :

En informatique, un Shebang (également appelé hashbang, hashpling, pound bang ou crunchbang) fait référence aux caractères "#!" quand ils sont les deux premiers caractères d'une directive interpréteur en tant que première ligne d'un fichier texte. Dans un système d'exploitation de type Unix, le chargeur de programmes prend la présence de ces deux caractères pour indiquer que le fichier est un script et tente d'exécuter ce script à l'aide de l'interpréteur spécifié par le reste de la première ligne du fichier.

Voir aussi l'entrée nix FAQ .

Même sous Windows, où la ligne Shebang ne détermine pas l'interprète à exécuter, vous pouvez transmettre des options à l'interprète en les spécifiant sur la ligne Shebang. Je trouve utile de conserver une ligne Shebang générique dans des scripts uniques (tels que ceux que j'écris en répondant à des questions sur SO), afin de pouvoir les tester rapidement sous Windows et ArchLinux .

Le tilitaire env vous permet d’appeler une commande sur le chemin:

Le premier argument restant spécifie le nom du programme à appeler; il est recherché en fonction de la variable d'environnement PATH. Tous les arguments restants sont passés comme arguments à ce programme.

251
Sinan Ünür

Pour développer un peu les autres réponses, voici un petit exemple de la façon dont vos scripts de ligne de commande peuvent être gênés par une utilisation imprudente des lignes /usr/bin/env Shebang:

$ /usr/local/bin/python -V
Python 2.6.4
$ /usr/bin/python -V
Python 2.5.1
$ cat my_script.py 
#!/usr/bin/env python
import json
print "hello, json"
$ PATH=/usr/local/bin:/usr/bin
$ ./my_script.py 
hello, json
$ PATH=/usr/bin:/usr/local/bin
$ ./my_script.py 
Traceback (most recent call last):
  File "./my_script.py", line 2, in <module>
    import json
ImportError: No module named json

Le module json n'existe pas dans Python 2.5.

Une façon de vous protéger contre ce type de problème consiste à utiliser les noms de commande versionnés python généralement installés avec la plupart des Pythons:

$ cat my_script.py 
#!/usr/bin/env python2.6
import json
print "hello, json"

Si vous avez juste besoin de distinguer entre Python 2.x et Python 3.x, les versions récentes de Python 3 fournissent également un nom python3:

$ cat my_script.py 
#!/usr/bin/env python3
import json
print("hello, json")
145
Ned Deily

Pour exécuter le script python, nous devons dire trois choses au shell:

  1. Que le fichier est un script
  2. Quel interprète nous voulons exécuter le script
  3. Le parcours de l'interprète

Le Shebang #! accomplit (1.). Le Shebang commence par un # car le caractère # est un marqueur de commentaire dans de nombreux langages de script. Le contenu de la ligne Shebang est donc automatiquement ignoré par l'interpréteur.

La commande env accomplit (2.) et (3.). Pour citer "grawity"

Une utilisation courante de la commande env consiste à lancer des interpréteurs, en utilisant le fait qu'env cherche dans $ PATH la commande à lancer. Étant donné que la ligne Shebang nécessite la spécification d’un chemin absolu, et que la localisation de divers interprètes (Perl, bash, python) peut varier beaucoup, il est courant d’utiliser:

#!/usr/bin/env Perl au lieu d'essayer de deviner s'il s'agit de/bin/Perl,/usr/bin/Perl,/usr/local/bin/Perl,/usr/local/pkg/Perl,/serveur de fichiers/usr/bin/Perl ou/home/MrDaniel/usr/bin/Perl sur le système de l'utilisateur ...

D'autre part, env est presque toujours dans/usr/bin/env. (Sauf dans les cas où ce n'est pas le cas; certains systèmes peuvent utiliser/bin/env, mais cela arrive rarement et ne se produit que sur des systèmes non Linux.)

82
Rose Perrone

Peut-être que votre question est dans ce sens:

Si vous voulez utiliser: $python myscript.py

Vous n'avez pas du tout besoin de cette ligne. Le système appellera python, puis l'interprète python exécutera votre script.

Mais si vous avez l’intention d’utiliser: $./myscript.py

En l'appelant directement comme un programme normal ou un script bash, vous devez écrire cette ligne pour spécifier au système le programme utilisé pour l'exécuter (et la rendre également exécutable avec chmod 755).

41
user3765197

Techniquement, en Python, il ne s'agit que d'une ligne de commentaire.

Cette ligne n'est utilisée que si vous exécutez le script py à partir du shell (à partir de la ligne de commande). C'est ce qu'on appelle " Shebang !" , et il est utilisé dans diverses situations, pas seulement avec Python scripts.

Ici, il demande au shell de démarrer une version spécifique de Python (pour s’occuper du reste du fichier.

39
mjv

La raison principale à cela est de rendre le script portable dans tous les environnements de système d'exploitation.

Par exemple, sous mingw, les scripts python utilisent:

#!/c/python3k/python 

et sous distribution GNU/Linux, il est soit:

#!/usr/local/bin/python 

ou

#!/usr/bin/python

et sous le meilleur système commercial Unix sw/hw de tous (OS/X), il est:

#!/Applications/MacPython 2.5/python

ou sur FreeBSD:

#!/usr/local/bin/python

Cependant, toutes ces différences peuvent rendre le script portable à tous en utilisant:

#!/usr/bin/env python
37
Jonathan Cline IEEE

L'appel système exec du noyau Linux comprend shebangs (#!) de manière native

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 # comme 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 les octets. 7f 45 4c 46 (qui est également lisible par l'homme pour .ELF). Cela lit le fichier ELF, le met correctement en mémoire et lance un nouveau processus avec celui-ci. Voir aussi: Comment le noyau obtient-il un fichier binaire exécutable s'exécutant sous linux?

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 .

Je ne pense pas que POSIX précise les shebangs cependant: 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 se produire". macOS et FreeBSD semblent également l’appliquer.

PATH motivation de recherche

Probablement, une des principales motivations de l'existence de shebangs est le fait que sous Linux, nous souhaitons souvent exécuter des commandes à partir de PATH comme suit:

basename-of-command

au lieu de:

/full/path/to/basename-of-command

Mais alors, sans le mécanisme Shebang, comment Linux pourrait-il savoir comment lancer chaque type de fichier?

Coder en dur l'extension dans les commandes:

 basename-of-command.py

ou mettre en œuvre la recherche PATH sur chaque interprète:

python basename-of-command

serait une possibilité, mais le problème majeur est que tout se casse si nous décidons de reformuler la commande dans une autre langue.

Shebangs résolvent ce problème magnifiquement.

Il est probablement logique de souligner une chose qui a échappé à la plupart d'entre elles, ce qui peut empêcher une compréhension immédiate. Lorsque vous tapez python dans le terminal, vous ne fournissez normalement pas un chemin complet. Au lieu de cela, l'exécutable est examiné dans la variable d'environnement PATH. À son tour, lorsque vous souhaitez exécuter un programme Python directement, /path/to/app.py, vous devez indiquer au shell quel interpréteur utiliser (via le hashbang , ce que les autres contributeurs expliquent ci-dessus).

Hashbang attend un chemin complet vers un interprète. Ainsi, pour exécuter votre programme Python directement, vous devez fournir un chemin complet vers le fichier binaire Python qui varie de manière significative, notamment si vous utilisez virtualenv . Pour résoudre le problème de la portabilité, l'astuce avec /usr/bin/env est utilisée. Ce dernier est à l’origine destiné à modifier l’environnement sur place et à y exécuter une commande. Lorsqu'aucune modification n'est fournie, la commande est exécutée dans l'environnement actuel, ce qui aboutit à la même recherche PATH, qui fait l'affaire.

Source de pilexchange unix

21
saaj

C'est recommandé, proposé dans la documentation:

2.2.2. Exécutable Python Scripts

Sur les systèmes Unix BSD’ish, les scripts Python peuvent être rendus directement exécutables, comme les scripts Shell, en mettant la ligne

#! /usr/bin/env python3.2

from http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts

12

Il s'agit d'une convention du shell qui indique au shell quel programme peut exécuter le script.

#!/usr/bin/env python

se résout en un chemin d'accès au binaire Python.

11
Frank Krueger

Vous pouvez essayer ce problème en utilisant virtualenv

Voici test.py

#! /usr/bin/env python
import sys
print(sys.version)

Créer des environnements virtuels

virtualenv test2.6 -p /usr/bin/python2.6
virtualenv test2.7 -p /usr/bin/python2.7

activer chaque environnement puis vérifier les différences

echo $PATH
./test.py
9
Sercan

Il me semble que les fichiers fonctionnent de la même manière sans cette ligne.

Si tel est le cas, vous exécutez peut-être le programme Python sous Windows? Windows n'utilise pas cette ligne. Il utilise plutôt l'extension de nom de fichier pour exécuter le programme associé à l'extension de fichier.

Cependant en 2011, un "lanceur Python" a été développé qui imite (dans une certaine mesure) ce comportement de Linux pour Windows. Cela se limite à choisir quel interpréteur Python est exécuté - par exemple. pour choisir entre Python 2 et Python 3 sur un système où les deux sont installés. Le programme de lancement est éventuellement installé en tant que py.exe par Python installation et peut être associé à des fichiers .py afin que le programme de lancement vérifie cette ligne et lance à son tour le fichier Python version interprète.

8
Craig McQueen

Il spécifie simplement quel interprète vous souhaitez utiliser. Pour comprendre cela, créez un fichier via un terminal en effectuant touch test.py, puis tapez dans ce fichier les éléments suivants:

#!/usr/bin/env python3
print "test"

et faites chmod +x test.py pour rendre votre script exécutable. Après ceci, lorsque vous faites ./test.py, vous devriez recevoir une erreur disant:

  File "./test.py", line 2
    print "test"
               ^
SyntaxError: Missing parentheses in call to 'print'

parce que python3 ne supprt pas l'opérateur d'impression.

Maintenant, continuez et changez la première ligne de votre code en:

#!/usr/bin/env python2

et cela fonctionnera en imprimant test sur stdout, car python2 prend en charge l'opérateur d'impression. Alors maintenant, vous avez appris comment basculer entre les interprètes de script.

6
Pavel

Cela signifie davantage d'informations historiques qu'une réponse "réelle".

Rappelez-vous qu’à l’époque, vous aviez BEAUCOUP de systèmes d’exploitation similaires à Unix, dont les concepteurs avaient chacun leur propre idée de l’emplacement des éléments, et n’incluaient parfois pas Python, Perl, Bash ou beaucoup d’autres éléments GNU/Open Source du tout .

C'était même le cas de différentes distributions Linux. Sous Linux - pré-FHS [1], vous pourriez avoir python dans/usr/bin/ou/usr/local/bin /. Ou bien il n’a peut-être pas été installé, vous avez donc créé le vôtre et vous l'avez mis dans ~/bin

Solaris a été le pire sur lequel j'ai jamais travaillé, en partie lors de la transition de Berkeley Unix vers System V. Vous pourriez vous retrouver avec des éléments dans/usr /,/usr/local /,/usr/ucb,/opt/etc. pour certains vraiment longs chemins. Je garde des souvenirs de l'installation de chaque paquet dans son propre répertoire par Sunfreeware.com, mais je ne me souviens pas s'il y a un lien symbolique entre les fichiers binaires et/usr/bin.

Oh, et parfois/usr/bin était sur un serveur NFS [2].

Donc, l'utilitaire env a été développé pour résoudre ce problème.

Ensuite, vous pourriez écrire #!/bin/env interpreter et tant que le chemin était correct, les choses avaient une chance raisonnable de fonctionner. Bien sûr, raisonnable signifiait (pour Python et Perl) que vous aviez également défini les variables d'environnement appropriées. Pour bash/ksh/zsh cela a juste fonctionné.

C'était important parce que les gens passaient autour des scripts Shell (tels que Perl et python) et si vous aviez codé en dur/usr/bin/python sur votre poste de travail Red Hat Linux, cela aurait mal tourné sur un SGI ... eh bien, non , Je pense que IRIX a placé python au bon endroit. Mais sur une station Sparc, il pourrait ne pas fonctionner du tout.

Ma station Sparc me manque. Mais pas beaucoup. Ok, maintenant tu me cherches sur E-Bay. Bastages.

[1] Norme de hiérarchie du système de fichiers. https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2] Oui, et parfois les gens font encore des choses comme ça. Et non, je ne portais pas non plus de navet OR d’oignon à la ceinture.

6
Petro

Si vous exécutez votre script dans un environnement virtuel, dites venv, puis exécuter _which python_ tout en travaillant sur venv affichera le chemin d'accès à l'interpréteur Python:

~/Envs/venv/bin/python

Notez que le nom de l'environnement virtuel est incorporé dans le chemin d'accès à l'interpréteur Python. Par conséquent, le codage en dur de ce chemin dans votre script posera deux problèmes:

  • Si vous téléchargez le script dans un référentiel, vous forcez les autres utilisateurs à utiliser le même nom d'environnement virtuel . C'est s'ils identifient le problème en premier.
  • Vous ne pourrez pas exécuter le script sur plusieurs environnements virtuels même si tous les packages requis étaient dans d'autres environnements virtuels.

Par conséquent, pour ajouter à la réponse de Jonathan , le Shebang idéal est #!/usr/bin/env python, pas seulement pour la portabilité entre Les systèmes d'exploitation, mais aussi pour la portabilité entre les environnements virtuels!

5
Bruce Wayne

Compte tenu des problèmes de portabilité entre python2 et python3, vous devez toujours spécifier l'une ou l'autre version sauf si votre programme est compatible avec les deux.

Certaines distributions livrent python de manière symétrique à python3 depuis un moment - ne comptez pas sur python pour être python2.

Ceci est souligné par PEP 394 :

Afin de tolérer les différences entre plates-formes, tout nouveau code qui doit appeler l'interpréteur Python ne doit pas spécifier python, mais doit spécifier python2 ou python3 (ou les versions plus spécifiques de python2.x et python3.x ; voir le Notes de migration ). Cette distinction doit être faite en shebangs, lors de l'appel à partir d'un script Shell, lors d'un appel via l'appel system () ou lors d'un appel dans un autre contexte.

3
Zulan

Il indique à l'interprète la version de python avec laquelle exécuter le programme lorsque vous avez plusieurs versions de python.

2
Katie T