23 points par GN⁺ 2025-12-31 | Aucun commentaire pour le moment. | Partager sur WhatsApp
  • 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.

Aucun commentaire pour le moment.