Mit Go statt Python skripten
(lorentz.app)- Vorstellung eines Tricks, mit dem sich Go-Dateien direkt wie ausführbare Dateien ausführen lassen
- Wenn man in die erste Zeile
//usr/local/go/bin/go run "$0" "$@"; exitschreibt und die Datei ausführbar macht, kann man sie mit./script.gostarten - Diese Methode ist kein Shebang, sondern nutzt das Verhalten von POSIX, bei ENOEXEC auf
/bin/shzurückzufallen - Die Shell führt die erste Zeile als Befehl aus, während der Go-Compiler sie als
//-Kommentar erkennt und ignoriert - Mit
"$0"wird der Pfad zur Datei selbst übergeben, sodassgo rundas Skript baut und ausführt, und mit$@werden die Argumente weitergereicht - Gos starke Standardbibliothek und garantierte Abwärtskompatibilität machen es gut für Skripting geeignet; solange man Go 1.x verwendet, können Skripte über Jahrzehnte hinweg laufen
- Man kann die Komplexität der Abhängigkeitsverwaltung mit virtuellen Python-Umgebungen, pip/poetry/uv usw. vermeiden
Wie der falsche Shebang funktioniert
- Shebangs (
#!) geben den Interpreter über den Systemaufrufexecvean, aber die hier vorgestellte Technik ist kein Shebang - In die erste Zeile der Go-Quelldatei kommt
//usr/local/go/bin/go run "$0" "$@"; exit, darunter abpackage mainganz normaler Go-Code- Mit
chmod +x script.goerhält die Datei Ausführungsrechte und lässt sich als./script.gostarten
- Mit
- Mit
stracesieht man, dass die Shell beim Versuch,./script.goperexecveauszuführen, vom Kernel ENOEXEC (Exec format error) zurückbekommt- Nach ENOEXEC verwendet die Shell als Fallback
/bin/shund interpretiert die Datei als Shell-Skript - In der Shell ist
//kein Kommentar, sondern wird als Root-Pfad (/) interpretiert, daher lässt sich//usr/local/go/bin/goals regulärer Pfad ausführen
- Nach ENOEXEC verwendet die Shell als Fallback
- Deshalb wird die erste Zeile
//usr/local/go/bin/go run "$0" "$@"; exitvon der Shell als Befehl ausgeführt"$0"übergibt den Pfad der ausgeführten Datei; im Aufruf wird"$0"also zum Pfad vonscript.go, sodassgo rundie Datei selbst findet, baut und ausführt"$@"ist die Expansion der Positionsparameter ab dem ersten Argument und ermöglicht Aufrufe wie./script.go -f flag0 here are some args- Ohne
; exitwürdeshdie Go-Datei zeilenweise weiter interpretieren und bei Tokens wiepackagemit einem Fehler abbrechen
Warum Go sich gut für Skripting eignet
- Gos garantierte Abwärtskompatibilität ist die Schlüsselfunktion: Solange Go 1.x verwendet wird, laufen geschriebene Skripte langfristig weiter
- Die gut ausgebaute Standardbibliothek und integrierte Werkzeuge (Formatter, Linter usw.) werden ohne zusätzliche Konfiguration mitgeliefert, wodurch Teilen und Portabilität von Skripten maximiert werden
- Anders als bei Python muss man keine virtuellen Umgebungen oder verschiedene Paketmanager (pip, poetry, uv) erlernen, um Code auszuführen
- Durch die eingebauten Werkzeuge des Go-Ökosystems und die IDE-Integration lassen sich Formatter und Linter auch ohne
.pyprojectoderpackage.jsonstandardmäßig nutzen
- Wenn ein aktuelles Go installiert ist, lässt sich das Ganze auf jedem OS über Jahrzehnte hinweg ausführen
Vergleich mit anderen kompilierten Sprachen
- Rust kompiliert langsamer und hat eine schwächere Standardbibliothek, weshalb Abhängigkeiten praktisch unvermeidlich sind; zudem verlangsamt der Perfektionsanspruch die Entwicklung
- Java und JVM-Sprachen haben bereits Skriptsprachen auf JVM-Bytecode-Basis, und leichtgewichtiges Kotlin-Skripting kann ebenfalls eine Alternative sein
- Unter den kompilierten Sprachen besitzt Go die am besten geeigneten Eigenschaften für Skripting
Problem mit gopls-Formatierung und die Lösung
goplsverlangt ein Leerzeichen nach Kommentaren (//example→// example), wodurch die falsche Shebang-Zeile kaputtgeht- Mit dem zusätzlichen Leerzeichen wird aus
//usr/local/go/bin/gonämlich// usr/local/go/bin/go, was die Shell nicht mehr als Pfad erkennt - Lösung: Ein Vorschlag aus dem HN-Thread, bei dem statt
//ein/**/-Blockkommentar verwendet wird- geschrieben als
/*usr/local/go/bin/go run "$0" "$@"; exit; */ - Das Semikolon (
;) nachexitist Pflicht
- geschrieben als
1 Kommentare
Hacker-News-Kommentare
Der Punkt des Autors, er wolle sich nicht mit „pip vs poetry vs uv“ beschäftigen, wird eigentlich von uv direkt abgedeckt
Einschließlich PyPI-Abhängigkeiten reicht es, wenn die Python-Version und uv installiert sind
Link zur offiziellen uv-Dokumentation
#!/usr/bin/env -S uv run --python 3.14 --scriptDamit lädt uv die angegebene Version herunter und führt sie aus, auch wenn Python selbst nicht installiert ist
Wer zum ersten Mal mit Clojure in Berührung kommt, hört meist den Rat, Leiningen zu verwenden, bei Python findet man bei einer Suche dagegen venv, poetry, hatch, uv und vieles mehr
uv wird zwar zunehmend zum De-facto-Standard, ist aber noch nicht allgemein verbreitet
Ich habe früher einmal Go per apt installiert und dann wegen einer viel zu alten Version noch einmal neu installiert, aber das ließ sich deutlich schneller lösen
Das Problem mit virtuellen Umgebungen in Python bleibt weiterhin komplex
Ein in Rust geschriebenes OSS-Tool, das Python-Versionen und venv automatisch verwaltet
Man konfiguriert einfach
pyproject.tomlund führtpyflow main.pyaus; dann installiert und sperrt es Abhängigkeiten wie Cargo und gleicht auch automatisch die passende Python-Version für das Projekt anDamals waren Poetry und Pipenv populär, aber bei venv und Versionsverwaltung noch unzureichend
Meist nutze ich
uv add, und nur bei Bedarfuv pipAllerdings hat
uv pipweiterhin die gleichen Grenzen wie pip — die Auflösung von Abhängigkeiten hängt von der Installationsreihenfolge abuv pip install dep-aund danachdep-bzu installieren ist etwas anderes als die Reihenfolge umzudrehen oder beides auf einmal zu installierenDas ist eher ein Problem von pip, aber das Chaos der Python-Paketverwaltung besteht weiter
uv lädt sie selbst herunter
Go hat Shebang-Unterstützung ausdrücklich abgelehnt
Stattdessen wird empfohlen,
gorunzu verwendenMit einem POSIX-Trick wie
/// 2>/dev/null ; gorun "$0" "$@" ; exit $?lässt sich das ausführenNim, Zig und D lassen sich mit der Option
-runähnlich verwenden, und Swift, OCaml und Haskell können Dateien direkt ausführenLink zur zugehörigen Diskussion
go runyaegi auf GitHub
Der Beitrag „Ich will den Unterschied zwischen pip, poetry und uv nicht kennen, ich will einfach nur Code ausführen“ ist letztlich ein Problem des technischen Kenntnisstands
uv runund PEP 723 haben alle Probleme bereits gelöstuv runkamIch nutze Python seit über 20 Jahren, hatte aber immer Angst vor Codebasen mit externen Paketen oder venv
Dank
uv runhabe ich alle Firmenprojekte umgestellt, aber bei privaten Projekten bin ich schon zu Go gewechseltLangfristig bevorzuge ich statisch typisierte Sprachen
Nutzer wollen einfach, dass das Programm läuft
uv runund PEP 723 lösen das Problem, aber weil man uv erst kennen muss, bleibt die Einstiegshürde bestehenSolange uv nicht das offizielle Standardwerkzeug ist, werden viele Nutzer Python verlassen
Ich halte das für eine wirklich geniale Idee
Aber Scripting braucht ein anderes ergonomisches Gefühl als Software für die Auslieferung
bash ist improvisierter, Go eignet sich eher für Produktisierung, Python liegt dazwischen, Ruby ist näher an bash und Rust eher auf der Go-Seite
Skripte sind nützlich, wenn man OS-Befehle schnell für einmalige Aufgaben kombinieren will
Go fehlt diese Improvisiertheit
Ich wollte unter Debian eine einfache gtk-App mit uv ausführen; obwohl alle Abhängigkeiten stimmten, lief sie nicht und endete schließlich in einem Core Dump
Jedes Mal, wenn ich Python erneut ausprobiere, passiert so etwas
Go ist zwar umständlicher, funktioniert aber nach dem Kompilieren einfach
Der Kernpunkt ist, ob es in einer einzigen Datei abgeschlossen werden kann
Auch in Go sind 500-Zeilen-Skripte möglich, aber die Sprache selbst geht von mehreren Dateien und Modulen aus
Dass die bang-line nicht funktioniert, passt ebenfalls dazu
Wenn
go runohnehin temporäre Binärdateien erzeugt, halte ich es für besser, gleich zu bauen und nach/usr/local/binzu legenbash ist genauso eine Abstraktionsschicht über dem OS wie Python; es wirkt nur so, weil es die Standard-Shell ist
Vor allem in Richtung von Code, den Menschen trotz LLM-Erzeugung gut lesen können
Ich stimme zu, dass Nutzer, die Python zum ersten Mal sehen, den Unterschied zwischen pip, poetry und uv nicht kennen müssen
Aber wenn jemand über so ein Thema bloggt, sollte er zumindest wissen, dass uv das Problem löst
Uninformierte Kritik ist nicht überzeugend
Ich habe das Konzept von uv selbst noch nicht ganz verstanden und bin deshalb neugierig
Ich schreibe gern Skripte in Python
Man kann schnell etwas erledigen, und für einfache Aufgaben ist es gut, ohne sich um Typen oder Speicher kümmern zu müssen
Für große Anwendungen würde ich es aber nicht verwenden
Auf den meisten Systemen ist Python standardmäßig vorhanden, und für einfache Skripte reicht das aus
Wenn man bedenkt, dass Go erst installiert werden muss, erscheint mir Python mit uv eher sinnvoll
Wie der Autor selbst sagte, habe das Ganze „leicht trollig angefangen“, letztlich ist es also eine Frage der Go-Präferenz
Mit
node bla.jsist es erledigtMan muss wissen, was eine Funktion zurückgibt, und wenn man die Sprache gut kennt, verarbeitet man Grundtypen aus dem Gedächtnis
Das gilt für statisch typisierte Sprachen genauso
Wenn man andere Leute mitdenken will, sollte man keinen Code für die Auslieferung in Python schreiben
Ich hatte eine Kritik an Python erwartet, aber stattdessen war es eher ein nützlicher Tipp
Bei Sprachen, die
//als Kommentar verwenden, lässt sich dieser Trick übernehmenDas geht mit C/C++, Java, JavaScript, Rust, Swift, Kotlin, ObjC, D, F#, GLSL und anderen
Besonders interessant ist GLSL für Grafikdemos in einer einzigen Datei
Shadertoy-Beispiel
In C kann man mit Blockkommentaren auch etwas wie
/*/../usr/bin/env gcc "$0" "$@"; ./a.out; rm -vf a.out; exit; */verwendenDas ist gewissermaßen ein uv für Swift
Swift unterstützt Shebang auch offiziell
#!direkt verwendenZu alten TCC-Zeiten habe ich so etwas als „C-Scripting“ genutzt
In größeren Projekten gab es eine Struktur, bei der ein Build-Skript das Manifest las, baute und danach ausführte
Wegen der schwierigen Kontrolle über die Umgebung ist das für die Praxis aber ungeeignet
Es unterstützt Shebang direkt
Wer eine ergonomischere Sprache möchte, kann auch die Funktion „run file directly“ in .NET 10 nutzen
Sie unterstützt Shebang und installiert Pakete innerhalb des Skripts automatisch
Mit der Direktive
#:sdkkann man sogar Web-Apps sofort ausführenNur die AOT-Kompilierung wirkt noch etwas roh
Zuerst dachte ich, das sei Python-Kritik, aber stattdessen hat es mich über die Richtung von Sprachökosystemen nachdenken lassen
Dass ML an Python gebunden wurde, halte ich für einen großen Fehler
Es ist langsam, sein Typsystem ist unpraktisch, und Deployment ist schwierig
Jetzt sollte man Alternativen wie TypeScript, Go und Rust in Betracht ziehen
Der Grund, warum ML auf Python gesetzt hat, war allerdings die C-basierte FFI
NodeJS, Rust und Go sind bei FFI schwach
Python hat hier eine Stärke
Ideal wäre eine Sprache, die so einfach wie Python ist, aber ein besseres Typsystem und ein besseres Deployment-Modell hat
Ich möchte Python nicht durch eine Sprache ersetzen, die aus dem JS-Ökosystem kommt
Lisp oder Lua (Torch) wären geeigneter gewesen, aber wegen der Einfachheit fiel die Wahl auf Python
Ich entwickle selbst ein Lisp-basiertes ML-Framework, aber ich glaube nicht, dass die Akzeptanz leicht sein wird
Versionskompatibilitätsprobleme, fehlendes semver und ein instabiles Ökosystem lassen es im Vergleich zu JS rückständig wirken
JS/Node ist in den letzten zehn Jahren gereift, Python steckt dagegen immer noch im Jahr 2012 fest
Dass ML auf Python standardisiert wurde, ist wirklich bedauerlich
Beim Erstellen von CLI-Tools ist Go viel schneller als Python
Wegen des Unterschieds bei den LOC bin ich zwar wieder zu Python zurückgekehrt, aber jedes Mal beim Ausführen vermisse ich Go
Vermutlich wäre OCaml ideal, aber die altmodischen Tooling-Werkzeuge sind belastend
Das Problem bei Go-Skripten ist, dass die erste Zeile keinen Leerraum enthalten darf
Weil
goplsautomatische Formatierung erzwingtAuch in CI muss die Formatkonsistenz gewahrt bleiben, daher ist das in der Praxis wichtig
Das größere Problem ist aber, dass man go.mod nicht verwenden kann
Das heißt, man kann Abhängigkeitsversionen nicht festlegen, wodurch die Kompatibilitätsgarantie schwächer wird