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

1048

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

John Garcias
la source
1
La réponse ci-dessous indique qu'il ne s'agit que d'une ligne de commentaire. Ce n'est pas toujours le cas. J'ai un "Bonjour, monde!" Script CGI (.py) qui ne fonctionnera et affichera la page Web qu'en #!/usr/bin/env pythonhaut.
Chakotay
Ils peuvent courir, mais pas dans l'environnement prévu
Nicholas Hamilton
18
J'ai visité ce poste tant de fois en 7 ans parce que j'oublie parfois l'env hashbang. Copier les pâtes :)
BugHunterUK

Réponses:

1084

Si vous avez plusieurs versions de Python installées, vous vous /usr/bin/envassurerez que l'interpréteur utilisé est le premier sur votre environnement $PATH. L'alternative serait de coder en dur quelque chose comme #!/usr/bin/python; c'est ok, mais moins flexible.

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

Si vous parlez d'autres plates-formes, bien sûr, cette règle ne s'applique pas (mais cette "ligne shebang" ne fait aucun mal, et vous aidera si vous copiez jamais ce script sur une plate-forme avec une base Unix, telle que Linux, Mac , etc).

Alex Martelli
la source
267
Juste pour ajouter: cela s'applique lorsque vous l'exécutez sous Unix en le rendant exécutable ( chmod +x myscript.py) puis en l'exécutant directement:, ./myscript.pyplutôt que simplement python myscript.py.
Craig McQueen
28
l'utilisation envdonne une flexibilité maximale dans la mesure où l'utilisateur peut sélectionner l'interpréteur à utiliser en changeant le CHEMIN. Souvent, cette flexibilité n'est cependant pas requise et l'inconvénient est que Linux par exemple ne peut pas utiliser le nom du script pour le nom du processus dans pset revient à "python". Lors du packaging d'applications python pour des distributions par exemple, je vous déconseille de les utiliser env.
pixelbeat
9
pyle lanceur peut utiliser la ligne shebang sous Windows. Il est inclus dans Python 3.3 ou il peut être installé indépendamment .
jfs
6
Un avertissement important, la valeur de retour d'env expire finalement. Ce qui est peu susceptible de vous affecter si vous exécutez des processus de courte durée. Cependant, j'ai eu des processus qui meurent avec le message /usr/bin/env: Key has expiredaprès plusieurs heures.
malaverdiere
4
@malaverdiere pouvez-vous créer un lien vers des ressources expliquant ce comportement d'expiration? Je ne les trouve pas.
Michael
267

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

En informatique, un shebang (également appelé hashbang, hashpling, pound bang ou crunchbang) fait référence aux caractères "#!" lorsqu'ils sont les deux premiers caractères d'une directive interpréteur comme première ligne d'un fichier texte. Dans un système d'exploitation de type Unix, le programme de chargement prend la présence de ces deux caractères comme une indication que le fichier est un script et essaie 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 FAQ Unix .

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

L' utilitaire env vous permet d'appeler une commande sur le chemin:

Le premier argument restant spécifie le nom du programme à invoquer; il est recherché selon la PATHvariable d'environnement. Tous les arguments restants sont passés comme arguments à ce programme.

Sinan Ünür
la source
30
Facile à trouver avec Google - si l'on connaît les mots-clés (la "ligne shebang" est essentielle).
Arafangion
14
En fait, cette explication est plus claire que les autres références que j'ai vérifiées avec Google. Il est toujours plus agréable d'obtenir une explication d'un paragraphe ciblant la question, plutôt que de lire un manuel complet traitant de chaque utilisation potentielle.
Sam Goldberg
1
@Arafangion, vous trouverez probablement cette question utile. TL; DR: symbolhound.com
ulidtko
@ulidtko: Moteur de recherche intéressant, pensez à écrire une réponse pour que la question de john garcias ait une meilleure réponse.
Arafangion
1
"Même sous Windows, où la ligne shebang ne détermine pas l'interpréteur à exécuter, vous pouvez transmettre des options à l'interpréteur en les spécifiant sur la ligne shebang." C'est tout simplement faux; si une telle chose se produit, c'est parce que l'interpréteur lui-même traite la ligne shebang. Si l'interprète n'a pas de reconnaissance spéciale pour les lignes de shebang, alors rien de tel ne se produit. Windows ne fait rien avec les lignes de shebang. "Ce que vous décrivez peut-être dans ce cas est le lanceur python: python.org/dev/peps/pep-0397 .
Kaz
154

Développant un peu les autres réponses, voici un petit exemple de la façon dont vos scripts de ligne de commande peuvent avoir des ennuis en utilisant /usr/bin/envprudemment les lignes de 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 se prémunir contre ce type de problème consiste à utiliser les noms de commande python versionnés qui sont 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 faire la distinction entre Python 2.x et Python 3.x, les versions récentes de Python 3 fournissent également un python3nom:

$ cat my_script.py 
#!/usr/bin/env python3
import json
print("hello, json")
Ned Deily
la source
27
Hmm, ce n'est pas ce que j'ai retenu de ce post.
glenn jackman
1
Différence entre local et global. Si le which pythonrendement /usr/bin/python, un chemin de répertoire local pourrait être codé en dur: #!/usr/bin/python. Mais c'est moins flexible que celui #!/usr/bin/env pythonqui a une application globale.
noobninja
85

Afin d'exécuter le script python, nous devons dire au shell trois choses:

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

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

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

Une utilisation courante de la envcommande est de lancer des interprètes, en utilisant le fait que env recherchera $ PATH pour la commande qu'on lui demande de lancer. Étant donné que la ligne shebang nécessite la spécification d'un chemin absolu et que l'emplacement des divers interprètes (perl, bash, python) peut varier considérablement, 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, / fileserver / usr / bin / perl ou / home / MrDaniel / usr / bin / perl sur le système de l'utilisateur ...

D'un autre côté, 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 c'est une occasion assez rare et ne se produit que sur des systèmes non Linux.)

Rose Perrone
la source
1
"grawity" où?
Pacerier
44

Votre question est peut-être en ce sens:

Si vous souhaitez utiliser: $python myscript.py

Vous n'avez pas du tout besoin de cette ligne. Le système appellera python puis l'interpréteur 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 quel programme utiliser pour l'exécuter, (et aussi le rendre exécutable avec chmod 755)

user3765197
la source
ou vous pouvez écrire python3 myscript.py
vijay shanker
44

L' execappel système du noyau Linux comprend #!nativement shebangs ( )

Quand tu fais 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, le reste de la ligne est analysé par le noyau Linux, ce qui en fait un autre exec appel avec chemin /usr/bin/env pythonet fichier actuel 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 commencé avec différents octets, l' execappel système 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 les octets 7f 45 4c 46(qui se trouvent également être humains) lisible pour .ELF). Confirmons qu'en lisant les 4 premiers octets de /bin/ls, qui est un exécutable ELF:

head -c 4 "$(which ls)" | hd 

production:

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 démarre un nouveau processus avec lui. Voir aussi: Comment le noyau obtient-il un fichier binaire exécutable sous Linux?

Enfin, vous pouvez ajouter vos propres gestionnaires de 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 .

Je ne pense pas que POSIX spécifie des shebangs cependant: https://unix.stackexchange.com/a/346214/32558 , bien qu'il mentionne dans les sections sur la justification et sous la forme "si les scripts exécutables sont pris en charge par le système, quelque chose peut se produire". macOS et FreeBSD semblent également l'implémenter.

PATH motivation de recherche

Probablement, une grande motivation pour l'existence de shebangs est le fait que sous Linux, nous voulons souvent exécuter des commandes à partir PATHde:

basename-of-command

au lieu de:

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

Mais alors, sans le mécanisme shebang, comment Linux saurait-il lancer chaque type de fichier?

Codage en dur de l'extension dans les commandes:

 basename-of-command.py

ou implémentant la recherche PATH sur chaque interprète:

python basename-of-command

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

Shebangs résout magnifiquement ce problème.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
39

Techniquement, en Python, ce n'est qu'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). Ceci est connu comme le " Shebang !" , et il est utilisé dans diverses situations, pas seulement avec des scripts Python.

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

mjv
la source
Le shebang est un concept Unix. Il peut être utile de mentionner qu'il fonctionne également sur Windows si vous avez installé le lanceur Python py.exe . Cela fait partie d'une installation Python standard.
florisla
38

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

Par exemple sous mingw, les scripts python utilisent:

#!/c/python3k/python 

et sous distribution GNU / Linux c'est soit:

#!/usr/local/bin/python 

ou

#!/usr/bin/python

et sous le meilleur système commercial Unix sw / hw de tous (OS / X), c'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
Jonathan Cline IEEE
la source
2
Sous MacOSX, c'est aussi /usr/bin/python. Sous Linux, le Python installé par le système l'est aussi presque certainement /usr/bin/python(je n'ai jamais rien vu d'autre et cela n'aurait aucun sens). Notez qu'il peut y avoir des systèmes qui n'en ont pas /usr/bin/env.
Albert
1
Si vous êtes sur OSX et utilisez Homebrew et suivez leurs instructions d'installation par défaut, ce sera sous #! / Usr / local / bin / python
Will
@ Jean-PaulCalderone: Voir la réponse de saaj ci-dessous.
pydsigner
Mise à jour pour l'année 2018: Bare pythonn'est pas si portable, c'est l'interpréteur Python par défaut de la distribution. Arch Linux utilise par défaut Python 3 depuis longtemps et les distributions peuvent y penser aussi parce que Python 2 n'est pris en charge que jusqu'en 2020.
mati865
22

Il est probablement logique de souligner une chose que la plupart ont manqué, ce qui peut empêcher une compréhension immédiate. Lorsque vous tapez pythonterminal, vous ne fournissez normalement pas de chemin complet. Au lieu de cela, l'exécutable est recherché dans PATHla variable d'environnement. À 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 le chemin complet vers un interprète. Ainsi, pour exécuter votre programme Python directement, vous devez fournir un chemin complet vers le binaire Python qui varie considérablement, en particulier compte tenu de l'utilisation de virtualenv . Pour aborder la portabilité, l'astuce avec /usr/bin/envest utilisée. Ce dernier est à l'origine destiné à modifier l'environnement en place et à exécuter une commande dans celui-ci. Lorsqu'aucune modification n'est fournie, il exécute la commande dans l'environnement actuel, ce qui entraîne effectivement la mêmePATH recherche qui fait l'affaire.

Source depuis unix stackexchange

saaj
la source
14

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

#! / usr / bin / env python

résout un chemin vers le binaire Python.

Frank Krueger
la source
9

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
Sercan
la source
9

Il spécifie simplement quel interprète vous souhaitez utiliser. Pour comprendre cela, créez un fichier via le terminal en faisant touch test.py, puis tapez dans ce fichier ce qui suit:

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

et faites chmod +x test.pypour rendre votre script exécutable. Après cela, ./test.pyvous devriez obtenir une erreur indiquant:

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

car python3 ne prend pas en charge l'opérateur d'impression.

Maintenant, allez-y et changez la première ligne de votre code en:

#!/usr/bin/env python2

et cela fonctionnera, en imprimant testsur stdout, car python2 prend en charge l'opérateur d'impression. Ainsi, vous avez maintenant appris à basculer entre les interpréteurs de script.

Pavel
la source
9

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

Si oui, alors vous exécutez peut-être le programme Python sous Windows? Windows n'utilise pas cette ligne - à la place, il utilise 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 Linux pour Windows. Ceci se limite uniquement au choix de l'interpréteur Python à exécuter - par exemple pour sélectionner entre Python 2 et Python 3 sur un système où les deux sont installés. Le lanceur est éventuellement installé comme py.exepar l'installation Python et peut être associé à des .pyfichiers afin que le lanceur vérifie cette ligne et lance à son tour la version d'interpréteur Python spécifiée.

Craig McQueen
la source
6
Il pourrait également utiliser $ python myscript.py.
Sinan Ünür
J'ai fait l'erreur de ne pas avoir la ligne et d'utiliser python script.py, et un jour je l'ai fait ./myscript.py et tout a cessé de fonctionner, puis je me suis rendu compte que le système regarde le fichier comme un script shell au lieu d'un script python.
Guagua
8

Cela signifie plus d'informations historiques qu'une réponse «réelle».

Rappelez - vous que dans la journée vous eu beaucoup de unix comme les systèmes d' exploitation dont les concepteurs avaient tous leur propre idée de l' endroit où mettre des choses, et parfois ne comprennent pas Python, Perl, Bash, ou beaucoup d'autres GNU / Open Source substance du tout .

Cela était même vrai pour différentes distributions Linux. Sous Linux - pré-FHS [1] - vous pourriez avoir python dans / usr / bin / ou / usr / local / bin /. Ou il n'a peut-être pas été installé, alors vous avez construit le vôtre et le mettez dans ~ / bin

Solaris a été le pire sur lequel j'ai jamais travaillé, en partie lors de la transition de Berkeley Unix au système V. Vous pouvez vous retrouver avec des choses dans / usr /, / usr / local /, / usr / ucb, / opt / etc. pour certains très longs chemins. J'ai des souvenirs de ce que Sunfreeware.com a installé chaque paquet dans son propre répertoire, mais je ne me souviens pas s'il a lié les binaires dans / usr / bin ou non.

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

L' envutilitaire a donc été développé pour contourner ce problème.

Ensuite, vous pouviez écrire #!/bin/env interpreteret tant que le chemin était correct, les choses avaient une chance raisonnable de courir. 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 (comme perl et python) et si vous aviez codé en dur / usr / bin / python sur votre poste de travail Red Hat Linux, ça allait mal se passer sur un SGI ... enfin, non , Je pense que IRIX a mis le 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 fais traîner 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 trucs comme ça. Et non, je ne portais ni navet NI oignon à la ceinture.

Petro
la source
5

Si vous exécutez votre script dans un environnement virtuel, par exemple venv, l'exécution which pythonen travaillant sur venvaffichera le chemin vers 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 entraînera deux problèmes:

  • Si vous téléchargez le script dans un référentiel, vous forcez les autres utilisateurs à avoir 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 vous disposiez de tous les packages requis dans d'autres environnements virtuels.

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

Bruce Wayne
la source
3

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

Certaines distributions expédiez un pythonlien symbolique à python3un certain temps maintenant - ne comptez pas sur l' pythonêtrepython2 .

C'est ce que souligne le 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 plutôt spécifier python2 ou python3 (ou les versions plus spécifiques python2.x et python3.x; voir les notes de migration ) . Cette distinction doit être faite dans les shebangs, lors de l'appel à partir d'un script shell, lors de l'appel via l'appel system (), ou lors de l'appel dans tout autre contexte.

Zulan
la source
2

Il indique à l'interpréteur avec quelle version de python exécuter le programme lorsque vous avez plusieurs versions de python.

Katie T
la source
0

Il vous permet de sélectionner l'exécutable que vous souhaitez utiliser; ce qui est très pratique si vous avez peut-être plusieurs installations python et différents modules dans chacun et que vous souhaitez choisir. par exemple

#!/bin/sh
#
# Choose the python we need. Explanation:
# a) '''\' translates to \ in shell, and starts a python multi-line string
# b) "" strings are treated as string concat by python, shell ignores them
# c) "true" command ignores its arguments
# c) exit before the ending ''' so the shell reads no further
# d) reset set docstrings to ignore the multiline comment code
#
"true" '''\'
PREFERRED_PYTHON=/Library/Frameworks/Python.framework/Versions/2.7/bin/python
ALTERNATIVE_PYTHON=/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
FALLBACK_PYTHON=python3

if [ -x $PREFERRED_PYTHON ]; then
    echo Using preferred python $ALTERNATIVE_PYTHON
    exec $PREFERRED_PYTHON "$0" "$@"
elif [ -x $ALTERNATIVE_PYTHON ]; then
    echo Using alternative python $ALTERNATIVE_PYTHON
    exec $ALTERNATIVE_PYTHON "$0" "$@"
else
    echo Using fallback python $FALLBACK_PYTHON
    exec python3 "$0" "$@"
fi
exit 127
'''

__doc__ = """What this file does"""
print(__doc__)
import platform
print(platform.python_version())
Neil McGill
la source
-8

cela indique au script où se trouve le répertoire python!

#! /usr/bin/env python
Bhargav Rao
la source