Utiliser Go pour le scripting à la place de Python
(lorentz.app)- Présentation d’une astuce permettant d’exécuter directement un fichier Go comme un exécutable
- En plaçant
//usr/local/go/bin/go run "$0" "$@"; exitsur la première ligne et en ajoutant les droits d’exécution, il devient possible de lancer le fichier avec./script.go - Cette méthode n’utilise pas un shebang, mais le comportement de repli de POSIX vers /bin/sh lorsqu’un ENOEXEC survient
- Le shell exécute la première ligne comme une commande, tandis que le compilateur Go l’ignore en la traitant comme un commentaire
// - Avec
"$0", le chemin du script lui-même est transmis, ce qui permet àgo runde construire et d’exécuter le script, et"$@"sert à transmettre les arguments - La puissante bibliothèque standard de Go et sa garantie de compatibilité ascendante le rendent adapté au scripting ; tant qu’on utilise une version Go 1.x, le script peut continuer à fonctionner pendant des décennies
- Cela permet d’éviter la complexité de la gestion des dépendances comme les environnements virtuels Python,
pip/poetry/uv, etc.
Fonctionnement du faux shebang
- Un shebang (
#!) sert à désigner l’interpréteur via l’appel systèmeexecve, mais la technique présentée ici n’est pas un shebang - Le principe consiste à mettre
//usr/local/go/bin/go run "$0" "$@"; exitsur la première ligne d’un fichier source Go, puis à écrire du code Go classique aprèspackage main- En donnant les droits d’exécution avec
chmod +x script.go, on peut le lancer directement comme./script.go
- En donnant les droits d’exécution avec
- En observant avec
strace, on voit que lorsque le shell tente d’exécuter./script.goviaexecve, le noyau renvoie ENOEXEC (Exec format error)- Après réception de ENOEXEC, le shell utilise
/bin/shcomme solution de repli pour interpréter le fichier comme un script shell - Dans le shell,
//n’est pas un commentaire mais est interprété comme un chemin racine (/), donc//usr/local/go/bin/goest exécuté comme un chemin valide
- Après réception de ENOEXEC, le shell utilise
- La première ligne
//usr/local/go/bin/go run "$0" "$@"; exitest donc exécutée comme une commande par le shell"$0"transmet le chemin du fichier exécuté ; dans la commande,"$0"devient donc le chemin descript.go, ce qui permet àgo runde retrouver, construire et exécuter son propre fichier source"$@"développe les paramètres positionnels à partir du premier argument, ce qui permet des appels comme./script.go -f flag0 here are some args- Sans
; exit,shcontinuerait à interpréter le fichier Go ligne par ligne et finirait par produire une erreur sur des tokens commepackage
Pourquoi Go est adapté au scripting
- La garantie de compatibilité ascendante de Go est l’élément clé : tant qu’on utilise Go 1.x, un script écrit aujourd’hui peut fonctionner longtemps
- Une bibliothèque standard mature et des outils intégrés (formatter, linter, etc.) sont fournis sans configuration supplémentaire, ce qui maximise le partage et la portabilité des scripts
- Contrairement à Python, il n’est pas nécessaire d’apprendre les environnements virtuels ni divers gestionnaires de paquets (
pip,poetry,uv) pour exécuter le code - Les outils intégrés de l’écosystème Go et leur intégration dans les IDE permettent d’utiliser formatter et linter par défaut, même sans
.pyprojectnipackage.json
- Contrairement à Python, il n’est pas nécessaire d’apprendre les environnements virtuels ni divers gestionnaires de paquets (
- Dès lors que la dernière version de Go est installée, les scripts peuvent s’exécuter pendant des décennies sur n’importe quel OS
Comparaison avec d’autres langages compilés
- Rust compile plus lentement, dispose d’une bibliothèque standard plus limitée, impose souvent l’usage de dépendances et sa recherche de perfection tend à ralentir le développement
- Java et les langages de la JVM disposent déjà de langages de scripting basés sur le bytecode JVM, et le scripting Kotlin léger peut aussi être une alternative
- Parmi les langages compilés, Go possède les caractéristiques les plus adaptées à un usage de scripting
Problème de formatage avec gopls et solution
goplsimpose un espace après un commentaire (//example→// example), ce qui casse la ligne de faux shebang- Si un espace est ajouté,
// usr/local/go/bin/gon’est plus reconnu comme un chemin par le shell - Solution : utiliser, comme proposé dans le fil HN, un commentaire de bloc
/**/à la place de//- Sous la forme
/*usr/local/go/bin/go run "$0" "$@"; exit; */ - Le point-virgule (
;) aprèsexitest indispensable
- Sous la forme
1 commentaires
Réactions sur Hacker News
Le passage où l’auteur dit « je ne veux pas me soucier de pip vs poetry vs uv » est en fait un cas d’usage que uv prend directement en charge
Y compris avec les dépendances PyPI, il suffit d’avoir la version de Python et uv installés
Lien vers la documentation officielle de uv
#!/usr/bin/env -S uv run --python 3.14 --scriptDe cette façon, même si Python lui-même n’est pas installé, uv télécharge la version demandée et l’exécute
Quand on découvre Clojure, on entend généralement qu’il faut utiliser Leiningen, alors qu’avec Python, une recherche renvoie venv, poetry, hatch, uv, etc.
uv est en train de devenir la solution dominante, mais ce n’est pas encore universel
J’ai déjà installé Go via apt avant de me rendre compte que la version était trop ancienne et de devoir le réinstaller, mais ce problème s’est réglé bien plus vite
La question des environnements virtuels Python reste compliquée
C’est un outil OSS écrit en Rust qui gère automatiquement la version de Python et le venv
Il suffit de configurer
pyproject.tomlpuis d’exécuterpyflow main.pypour qu’il installe et verrouille les dépendances comme Cargo, tout en ajustant automatiquement la bonne version de Python pour le projetÀ l’époque, Poetry et Pipenv étaient populaires, mais ils restaient insuffisants pour la gestion du venv et des versions
J’utilise surtout
uv add, et seulementuv pipquand c’est nécessaireMais
uv pipconserve les limites de pip — la résolution des dépendances change selon l’ordre d’installationInstaller
dep-aavecuv pip install dep-apuisdep-b, inverser l’ordre, ou tout installer d’un coup, ce n’est pas la même choseC’est davantage un problème de pip, mais le chaos de la gestion des paquets Python reste bien présent
uv la télécharge automatiquement
Go a explicitement refusé la prise en charge du shebang
À la place, il est recommandé d’utiliser
gorunOn peut l’exécuter avec une astuce POSIX comme
/// 2>/dev/null ; gorun "$0" "$@" ; exit $?Nim, Zig et D peuvent faire quelque chose de similaire avec l’option
-run, et Swift, OCaml et Haskell peuvent exécuter directement un fichierLien vers la discussion associée
go runyaegi GitHub
Le propos « je ne veux pas connaître la différence entre pip, poetry et uv, je veux juste exécuter le code » relève au fond d’une question de maîtrise technique
uv runet PEP 723 ont déjà résolu tout le problèmeuv runJ’utilise Python depuis plus de 20 ans, mais les codebases avec paquets externes ou venv m’ont toujours fait peur
Grâce à
uv run, tous les projets de mon entreprise ont migré, mais pour mes projets personnels je suis déjà passé à GoÀ long terme, je préfère les langages à typage statique
Les utilisateurs veulent simplement que le programme s’exécute
uv runet PEP 723 règlent le problème, mais il faut encore connaître uv, ce qui laisse une barrière à l’entréeTant que uv ne sera pas l’outil officiel par défaut, beaucoup d’utilisateurs abandonneront Python
Je trouve que c’est une idée vraiment géniale
Mais le scripting exige une ergonomie différente de celle d’un logiciel destiné au déploiement
bash est improvisé, Go est adapté à la production, Python se situe quelque part entre les deux, Ruby est plus proche de bash, et Rust du côté de Go
Les scripts sont utiles pour combiner rapidement des commandes OS et traiter des tâches ponctuelles
Go manque de cette spontanéité
J’ai essayé de lancer une petite appli gtk sur Debian avec uv, toutes les dépendances semblaient correctes, et malgré cela l’exécution a échoué avec un Core Dump
Ça se reproduit à chaque fois que je redonne sa chance à Python
Go est verbeux, mais une fois compilé, ça fonctionne, tout simplement
Le point clé, c’est de savoir si tout peut tenir dans un seul fichier
On peut écrire un script de 500 lignes en Go, mais le langage part du principe qu’il y aura plusieurs fichiers et des modules
L’absence de bang-line va dans ce sens
Puisque
go runproduit de toute façon un binaire temporaire, je trouve qu’il vaut mieux compiler directement et le mettre dans/usr/local/binbash est lui aussi une couche d’abstraction au-dessus de l’OS, autant que Python, on le ressent juste différemment parce que c’est le shell par défaut
Surtout si l’on veut que le code écrit par un LLM reste facile à lire pour des humains
Je suis d’accord sur le fait qu’un débutant en Python n’a pas besoin de connaître la différence entre pip, poetry et uv
En revanche, pour un blogueur qui écrit sur ce sujet, il devrait au minimum savoir que uv résout ce problème
Une critique fondée sur l’ignorance n’est pas convaincante
Comme je n’ai pas encore complètement compris le concept de uv, ça m’intéresse aussi
Moi, j’aime écrire des scripts en Python
On peut travailler vite, et c’est pratique pour les tâches simples sans se soucier des types ni de la mémoire
Mais je n’ai pas envie de l’utiliser pour de grosses applications
La plupart des systèmes ont déjà Python par défaut, ce qui suffit pour de petits scripts
Si l’alternative est d’installer Go, je préfère encore utiliser Python avec uv
Comme l’auteur l’a dit lui-même, « j’ai commencé un peu pour troller » ; au final, c’est surtout une question de préférence pour Go
Avec
node bla.js, c’est régléIl faut savoir ce que renvoie une fonction, et quand on connaît bien le langage, on gère les types de base de mémoire
C’est pareil dans les langages à typage statique
Si on tient compte des autres, il ne faut pas écrire en Python du code destiné à être distribué
Je m’attendais à une critique de Python, et au final c’était plutôt une astuce utile
Si le langage utilise
//pour les commentaires, on peut reprendre cette astuceC’est possible avec C/C++, Java, JavaScript, Rust, Swift, Kotlin, ObjC, D, F#, GLSL, etc.
L’idée de faire une démo graphique en un seul fichier avec GLSL est particulièrement intéressante
Exemple Shadertoy
En C, on peut aussi utiliser un commentaire de bloc comme
/*/../usr/bin/env gcc "$0" "$@"; ./a.out; rm -vf a.out; exit; */C’est un concept similaire à uv pour Swift
Swift prend aussi officiellement en charge le shebang
#!À l’époque de TCC, j’utilisais ce genre de méthode pour faire du « scripting en C »
Sur les gros projets, le script de build lisait un manifeste puis construisait et exécutait le tout
Mais comme il est difficile de contrôler l’environnement, ce n’est pas adapté à un usage professionnel
Il prend directement en charge le shebang
Si vous voulez un langage plus ergonomique, .NET 10 propose aussi la fonctionnalité « run file directly »
Elle prend en charge le shebang et installe automatiquement les paquets dans le script
Avec la directive
#:sdk, on peut même exécuter immédiatement une web appCela dit, la compilation AOT reste encore un peu brute
Au départ, je pensais lire une critique de Python, mais cela m’a plutôt poussé à réfléchir à la direction prise par l’écosystème des langages
À mes yeux, le fait que le ML soit lié à Python a été une grosse erreur
C’est lent, le système de types est inconfortable, et le déploiement est difficile
Il faut maintenant envisager des alternatives comme TypeScript, Go ou Rust
Cela dit, si le ML a choisi Python, c’est à cause de la FFI basée sur C
NodeJS, Rust et Go sont plus faibles sur ce terrain
Python a ici un vrai avantage
L’idéal serait un langage aussi simple que Python, mais avec un meilleur système de types et un meilleur modèle de déploiement
Je n’ai pas envie de remplacer Python par un langage issu de l’écosystème JS
Lisp ou Lua (Torch) étaient plus adaptés, mais Python a été choisi pour sa simplicité
Je développe moi-même un framework ML basé sur Lisp, mais je doute qu’il soit largement adopté
Problèmes de compatibilité de versions, absence de semver, écosystème instable… on a l’impression que Python est en retard même par rapport à JS
JS/Node a mûri ces dix dernières années, alors que Python semble toujours bloqué en 2012
Le fait que le ML se soit standardisé sur Python est vraiment regrettable
Pour créer des outils CLI, Go est bien plus rapide que Python
Je reviens à Python à cause de la différence en nombre de lignes, mais Go me manque à chaque exécution
OCaml serait probablement idéal, mais son outillage vieillissant me freine
Le problème des scripts Go, c’est que la première ligne ne doit contenir aucun espace
Parce que
goplsimpose le formatage automatiqueEt comme il faut aussi garder un format cohérent en CI, c’est important en pratique
Mais le vrai problème, plus grave encore, c’est qu’on ne peut pas utiliser go.mod
Autrement dit, on ne peut pas fixer les versions des dépendances, donc les garanties de compatibilité sont plus faibles