- 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" "$@"; exit sur 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 run de 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ème execve, mais la technique présentée ici n’est pas un shebang
- Le principe consiste à mettre
//usr/local/go/bin/go run "$0" "$@"; exit sur la première ligne d’un fichier source Go, puis à écrire du code Go classique après package main
- En donnant les droits d’exécution avec
chmod +x script.go, on peut le lancer directement comme ./script.go
- En observant avec
strace, on voit que lorsque le shell tente d’exécuter ./script.go via execve, le noyau renvoie ENOEXEC (Exec format error)
- Après réception de ENOEXEC, le shell utilise
/bin/sh comme 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/go est exécuté comme un chemin valide
- La première ligne
//usr/local/go/bin/go run "$0" "$@"; exit est 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 de script.go, ce qui permet à go run de 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, sh continuerait à interpréter le fichier Go ligne par ligne et finirait par produire une erreur sur des tokens comme package
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
.pyproject ni package.json
- 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
gopls impose 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/go n’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ès exit est indispensable
Aucun commentaire pour le moment.