#! placée tout en haut du fichier pour dire au système quel interpréteur lancer. Je détaille ici quand utiliser #!/usr/bin/env python3, quand préférer un chemin absolu, comment rendre un script exécutable et pourquoi les environnements virtuels changent la donne. Je termine avec les erreurs que je vois le plus souvent dans les projets backend et les scripts d’outillage.
Les points à garder en tête avant de toucher à la première ligne
- La ligne
#!doit être tout en haut du fichier, sans caractère avant elle. - Sur Unix et Linux, il faut aussi donner le droit d’exécution avec
chmod +x. -
#!/usr/bin/env python3est généralement le choix le plus portable pour un script générique. - Dans un virtualenv, le chemin pointé est souvent absolu, ce qui améliore la fiabilité mais réduit la portabilité.
- Sur Windows, le lanceur Python sait interpréter plusieurs formes de shebang, mais la logique n’est pas identique à celle d’Unix.
Ce que fait vraiment la première ligne #!
Le point important, c’est que cette ligne n’est pas pensée d’abord pour Python. C’est le système d’exploitation qui la lit avant de lancer le fichier, puis qui délègue l’exécution à l’interpréteur indiqué. Autrement dit, le code Python ne “comprend” pas le shebang au sens strict du terme: il le tolère parce que le premier caractère # ouvre un commentaire.
La conséquence est simple: le début exact du fichier compte. Si un espace, une ligne vide, un BOM ou n’importe quel autre caractère précède #!, la détection peut échouer. Sur certains systèmes, la première ligne doit aussi finir par un retour à la ligne Unix (\n) et non par un retour Windows (\r\n). C’est un détail qui paraît minuscule jusqu’au jour où un script fonctionne chez vous et tombe en erreur sur le serveur.
Je retiens donc une règle pratique: le shebang sert à rendre un script autonome au niveau du lancement, pas à remplacer la logique Python elle-même. Une fois ce mécanisme clair, le vrai sujet devient le choix de l’interpréteur à pointer.
Le bon en-tête selon le contexte
Il n’existe pas une seule bonne ligne pour tous les cas. Je choisis différemment selon que le script est personnel, partagé dans une équipe ou installé comme commande. Dans les pratiques actuelles, je préfère aussi éviter le simple python sur Unix-like, parce que cette commande reste ambiguë selon les distributions et les versions disponibles.
| En-tête | Quand je l’utilise | Point fort | Limite |
|---|---|---|---|
#!/usr/bin/env python3 |
Script destiné à plusieurs machines Unix/Linux ou macOS | Recherche l’interpréteur dans le PATH, donc très flexible |
Dépend de env et du PATH de l’utilisateur |
#!/usr/bin/python3 |
Serveurs ou systèmes où le chemin est stable et connu | Moins de dépendance à la variable PATH
|
Moins portable d’une distribution à l’autre |
#!/chemin/vers/venv/bin/python |
Commande installée dans un environnement virtuel | Verrouille exactement le bon interpréteur | Le chemin est spécifique à l’environnement et donc non portable |
#!/usr/bin/env -S python3 -O |
Quand il faut passer plusieurs arguments à l’interpréteur | Permet de séparer proprement interpréteur et options | Je ne l’emploie que si la cible supporte bien env -S
|
Le choix le plus robuste pour un script “générique” reste souvent #!/usr/bin/env python3. En revanche, si je distribue un outil interne qui doit tourner avec un environnement précis, je préfère un chemin absolu généré par l’installation. C’est moins élégant visuellement, mais beaucoup plus fiable en production.
Ce compromis entre portabilité et déterminisme est le vrai cœur du sujet. Une fois qu’on l’a compris, le reste consiste surtout à rendre le fichier exécutable sans casser son comportement.

Rendre le script exécutable sans casser la portabilité
Pour exécuter un script directement, deux conditions doivent être réunies: le shebang doit être la toute première ligne et le fichier doit avoir le droit d’exécution. Sur Unix, c’est généralement ce duo qui transforme un fichier texte en commande.
#!/usr/bin/env python3
def main() -> None:
print("Script prêt à être lancé")
if __name__ == "__main__":
main()
Ensuite, j’applique le droit d’exécution avec chmod +x script.py, puis je lance le fichier avec ./script.py. Le point de départ du script change alors: on ne demande plus à la ligne de commande “d’ouvrir un fichier Python”, on lui dit “d’exécuter cette commande”. La nuance est importante, parce qu’elle explique pourquoi un script peut être lisible mais pas exécutable.
Je conseille aussi de garder une structure minimale propre, avec un point d’entrée explicite comme if __name__ == "__main__". Ce n’est pas le shebang qui en dépend, mais dans un projet backend cela évite de confondre un module importable avec un utilitaire lancé directement.
À partir du moment où le script dépend d’un environnement dédié, la question ne porte plus seulement sur l’exécution directe: elle touche aussi à l’isolation des dépendances.
Ce qui change avec les environnements virtuels
Dans un virtualenv, les scripts installés ne se contentent pas de chercher un python3 “quelconque” dans le PATH. Ils pointent souvent vers l’interpréteur exact de l’environnement, avec un chemin absolu. C’est ce qui garantit qu’un outil comme pytest, un script interne ou une commande installée par pip tourne avec le bon Python, même si l’environnement n’est pas activé dans le shell.
Je trouve ce comportement sain, parce qu’il réduit énormément les erreurs du type “ça marche sur ma machine”. En revanche, il a une contrepartie claire: un environnement virtuel est intrinsèquement peu portable si on le déplace tel quel. Si le dossier change d’emplacement, il vaut mieux recréer l’environnement plutôt que de tenter de le bricoler.
Sur Windows, le support des shebangs passe par le lanceur Python ou le gestionnaire d’installation Python. La logique n’est donc pas identique à celle d’Unix, mais l’idée reste la même: la première ligne aide à choisir le bon interpréteur, pas à magiquement rendre tous les fichiers exécutables de la même manière.
Dans la pratique, un shebang de venv est un indicateur fort: ce fichier appartient à un environnement donné. Cette précision m’aide à décider quand il faut archiver, quand il faut régénérer, et quand il vaut mieux distribuer une commande autrement.
Les erreurs que je vois le plus souvent
Les problèmes de shebang sont rarement spectaculaires; ils sont surtout agaçants, parce qu’ils ressemblent à de petits écarts de configuration. Voici ceux qui reviennent le plus souvent dans les projets réels.
-
Un caractère avant
#!: une ligne vide, un espace ou un encodage mal géré peut suffire à casser la détection. - Des fins de ligne Windows sur Unix : le fichier est lisible, mais le noyau ne le traite pas toujours comme prévu.
-
Un fichier non exécutable : sans
chmod +x, le script reste un simple fichier texte. -
Un interpréteur trop vague :
pythonpeut pointer vers une version inattendue, alors quepython3est plus explicite. -
Des arguments ajoutés sans
-S: si vous avez besoin d’options comme-O, il faut vérifier le support de/usr/bin/env -S. - Une attente trop forte côté Windows : le shebang aide, mais il ne remplace pas le lanceur ni les associations de fichiers.
- Une déclaration d’encodage mal placée : si le fichier n’est pas en UTF-8, la ligne d’encodage doit venir en second, juste après le shebang.
Le meilleur réflexe que j’ai pris avec le temps, c’est de tester le script dans le même contexte que celui de l’utilisateur final: même OS, même interpréteur, même environnement. C’est là qu’apparaissent les défauts qui restent invisibles dans un shell déjà “bien préparé”.
Une fois ces pièges identifiés, il devient plus simple de définir une règle d’équipe plutôt que de laisser chaque fichier improviser son propre mode de lancement.
Le réglage que je recommande dans un projet web
Pour un dépôt backend ou un petit outil de maintenance, je pars généralement sur #!/usr/bin/env python3 quand le script doit pouvoir vivre sur plusieurs machines Unix-like. C’est le meilleur équilibre entre lisibilité et souplesse. Si le script est installé dans un environnement virtuel ou distribué comme commande interne, je laisse au packaging le soin d’écrire un chemin plus précis.
- Pour un utilitaire partagé dans l’équipe, je privilégie un shebang clair, court et prévisible.
- Pour une commande livrée via un virtualenv, je fais confiance au chemin absolu généré à l’installation.
- Pour un projet qui touche aussi Windows, je ne compte pas uniquement sur la première ligne: je documente aussi la commande de lancement.
- Pour un script qui peut devenir durable, je pense rapidement en termes de packaging, pas seulement en termes de fichier exécutable.