- Wenn du Automatisierungsskripte in
~/bin/ ablegst und ausführst, lassen sich Python-Skripte mit uv und Inline-Skript-Metadaten schlanker handhaben
- Wenn
~/bin im PATH enthalten ist, kannst du als Shebang #!/usr/bin/env -S uv run --script festlegen und den Dateinamen wie einen Befehl ausführen
- Diese Konfiguration sorgt dafür, dass die Shell zuerst
uv run --script ausführt und die aktuelle Skriptdatei als Argument übergibt
- uv ignoriert die Shebang-Zeile und führt den restlichen Inhalt wie eine normale Python-Datei aus
- Mit
chmod a+x {filname} kannst du Ausführungsrechte vergeben und das Skript dann als einfaches Python-Kommandozeilen-Skript mit automatischer Abhängigkeitsverwaltung verwenden
uv als Shebang ausführen
- Für die Arbeitsautomatisierung werden oft mehrere Skripte im Verzeichnis
~/bin/ erstellt und verwendet
- Wenn
~/bin im PATH enthalten ist, kannst du Skripte direkt nur über ihren Dateinamen ausführen
- Die dabei verwendete Shebang-Zeile sieht so aus
#!/usr/bin/env -S uv run --script
Ausführungsablauf und Rechtevergabe
- Die obige Shebang-Zeile führt auf der Kommandozeile
uv run --script aus und übergibt die Skriptdatei als Argument
- uv ignoriert die Shebang-Zeile und führt den Rest der Datei als normalen Python-Code aus
- Vor der Ausführung musst du dem Skript Ausführungsrechte geben
chmod a+x {filname}
- Nach der Einrichtung kannst du ein einfaches Python-Kommandozeilen-Skript mit automatischer Abhängigkeitsverwaltung direkt ausführen
1 Kommentare
Hacker-News-Meinungen
Ich habe heute zum ersten Mal von env -S erfahren, und als ich den Shebang im Artikel sah, dachte ich sofort: „Das wird unter Linux nicht funktionieren, weil ein Shebang nur ein einziges Argument übergeben kann.“
Wenn man zum Beispiel
foo.pyausführt, das mit#!/usr/bin/env -S uv run --scriptbeginnt, übergibt das Betriebssystem die gesamte Shebang-Zeile tatsächlich als ein Argument, etwa/usr/bin/env '-S uv run --script' foo.py.Aber
-Svonenvzerlegt diesen String wieder in mehrere Argumente, sodass es funktioniert. Sehr elegant und nützlich.Setzt man nach
#!/usr/bin/guile \in die nächste Zeile-e main -sund schließt mit!#, wird daraus so etwas wie/usr/bin/guile -e main -s filename.Ich frage mich, warum man das ausgerechnet so gelöst hat. Vermutlich wurde
env -Serst kürzlich hinzugefügt oder war nicht auf allen Plattformen verfügbar, die relevant waren.https://www.gnu.org/software/guile/manual/html_node/The-Meta-Switch.html
make, die aber YAML für die Konfiguration nutzt.Man setzt
#!/usr/bin/env -s go run github.com/rliebz/tusk@latest -fals Shebang und verwendet als Interpreter die Go-Shell gosh, etwainterpreter: go run mvdan.cc/sh/v3/cmd/gosh@latest -s.So entsteht eine CLI, die auf jeder Architektur ausführbar ist, solange Go installiert ist.
nix-shell erlaubt zum Beispiel nach
#!/usr/bin/env nix-shelleine weitere Zeile#!nix-shell --pure -i runghc ./default.nix, um Abhängigkeiten und Argumente anzugeben; danach kann direkt Haskell-Code folgen.Die seltsamen Regeln zur Trennung von Leerzeichen in Shebang-Zeilen sind ein alter Bug, der zu einem nicht mehr behebbaren Makel von Unix geworden ist.
Jedes Mal, wenn man solche Workarounds nutzt, denke ich, dass man mit 30 Minuten Aufwand in den 1980ern jahrelangen Ärger hätte vermeiden können. Shebang-Zeilen hätten von Anfang an wie
/bin/shaufgeteilt werden sollen.Kein Shebang, aber ich nutze uv in letzter Zeit wie ein Installationstool.
Weil es schwierig ist, Python-CLI-Tools mit komplexen Abhängigkeiten zu paketieren und zu verteilen, habe ich es auf zwei Arten eingesetzt.
Die eine besteht darin, ein
curl | sh-Installationsskript zu kopieren und am Endeuv tool install --python python3.12 my-toolhinzuzufügen, sodass Nutzer die CLI mit einer einzigen Zeile installieren können. Die andere besteht darin, ein kleines PyPI-„Installer“-Paket zu erstellen, das nuruvals Abhängigkeit hat unduv tool installausführt.Beide Methoden installieren das CLI-Tool, Python 3.12 und alle Python-Abhängigkeiten in einer isolierten Umgebung. Nutzer müssen keine virtuelle Umgebung verwalten, und bei der
curl | sh-Variante muss nicht einmal Python installiert sein.Seitdem gibt es deutlich weniger GitHub-Issues, die dadurch entstehen, dass Nutzer komplexe Abhängigkeiten kaputtmachen.
Details und Code-Links: https://aider.chat/2025/01/15/uv.html
openssl-dev,gzip,ffiund muss Python kompilieren?Zumindest mit ASDF-VM habe ich wegen anderer Python-Installationstools intern solche Erfahrungen gemacht.
miseundaquascheinen ebenfalls ein nützliches Konzept zur Tool-Installation zu haben.Wo wir gerade bei interessanten Shebang-Geschichten sind: Ich habe so etwas gebaut, um Prolog-
.pl-Dateien aus der Shell auszuführen:/*usr/bin/env scryer-prolog "$0" "$@" ; exit #*/Für Skriptsprachen, die
/**/-Kommentare haben, aber#-Kommentare oder#!-Shebangs nicht direkt unterstützen, dürfte das im Großen und Ganzen funktionieren.Ursprünglicher Kommentar: https://github.com/mthom/scryer-prolog/issues/2170#issuecomment-1821713993
Diese Datei ist gleichzeitig eine gültige Shell-Datei und eine gültige Prolog-Datei. Wird sie von der Shell ausgeführt, sucht die erste Zeile nach dem Glob-Pattern
/*usr/bin/envund startet/usr/bin/env;envführt dannscryer-prolog "test.pl"aus. Auf Prolog-Seite ist die erste Zeile ein/* ... */-Kommentar und wird ignoriert.Danach führt die Shell nach dem
;einexitaus, damit der restliche Prolog-Code nicht als Shell interpretiert wird, und das#sorgt dafür, dass die Shell den Prolog-Kommentarabschluss*/ignoriert, wodurch Fehlermeldungen vermieden werden.Vielleicht das Beste und zugleich Schlimmste, was ich je gebaut habe.
Darüber hinaus kann man auch C-Programme mit einer ersten Zeile der Form
//usr/bin/env sh -c ...ausführbar machen. Beim Ausführen wird es mitmakegebaut und anschließend das resultierende Binary gestartet; wenn man also den Quellcode ändert und erneut ausführt, sieht man die geänderte Ausgabe sofort.Nach
#!/bin/shkann man in die nächste Zeile# the next line restarts using tcl \schreiben und dannexec tclsh "$0" "$@"ausführen lassen.Das nutzt aus, dass in TCL auch Kommentarzeilen mit Zeilenfortsetzung möglich sind.
In Trey Hunners Artikel Lazy self-installing Python scripts with uv gibt es ausführlichere Informationen mit Beispielen:
https://treyhunner.com/2024/12/lazy-self-installing-python-scripts-with-uv/
printf %.1s 0{1..80} $'\n'oderfor i in {1..80}; do echo -n 0; done; echo.Beim ffmpeg-Beispiel sehe ich auch nicht so recht, worin der Unterschied zu
ffmpeg -i in.mp4 -c:v copy -filter:a volumedetect -pass 1 -f null /dev/null && ffmpeg -i in.mp4 -c:v copy -filter:a "loudnorm" -pass 2 out.mp4besteht.Ehrlich gesagt wirkt die Python-Variante komplizierter.
Für kleine Aufgaben kann man diesen Trick zusammen mit mise (mise-en-place) verwenden: https://mise.jdx.dev/tasks/toml-tasks.html#shell-shebang
Dabei setzt man unter
toolsuv = 'latest', deklariert imrun-Block der Aufgabe#!/usr/bin/env -S uv run --scriptsowiedependencies = ["requests<3", "rich"]und fügt danach den Python-Code ein.Vor 16 Tagen gab es schon einen verwandten Artikel samt Diskussion: Uv's killer feature is making ad-hoc environments easy https://news.ycombinator.com/item?id=42676432 (502 Punkte, 417 Kommentare)
Ein nützlicher Trick, den man sich merken sollte.
Ich habe neulich meinen Python-Alias auf
uv run pythonumgestellt, und das ist ziemlich angenehm. Man muss keine.venvusw. verwalten, sich nicht um Systeminstallationen oder globale/benutzerspezifische Installationen kümmern, und unter Debian hilft es besonders.Auch wenn man in einem Projekt oder einer Umgebung eine REPL startet, kann man sie ohne
activateaufrufen, was die gedankliche Last reduziert.Ein Nachteil ist allerdings: Wenn man
.pydirekt mit uv aufruft, werden Projekt-/Umgebungsdateipfade anhand des aktuellen Arbeitsverzeichnisses bestimmt, nicht anhand des Speicherorts der.py-Datei.Es gibt zwar den expliziten Schalter
--project, sodass man etwauv run --project /script.pyverwenden kann, aber ein relativer Projekt-Schalter bezogen auf die Zieldatei wäre schön, weil er Wiederholungen reduzieren würde.Ich habe zwar ein paar Fallstricke erlebt, wenn man
uvoderuvxals Command-Runner verwendet, aber meistens funktioniert es gut – und dann spart es enorm viel Zeit.Als Python-Entwickler und neugieriger Mensch liegen in meinem Home-Verzeichnis überall flache Klone und Wegwerf-Testverzeichnisse herum; dass das diesen Verwaltungsaufwand reduziert, ist wirklich nützlich.
Diese Idee möchte ich mir abschauen. Ich frage mich, ob dafür eine eigene Erweiterung statt
.pyverwendet wird, oder ob schon ein.py-File mit gesetztem Execute-Bit und Shebang als Signal dient, dass es nicht mit dem System-Python, sondern direkt als Skript ausgeführt werden kann.uvselbst genutzt, um zu sehen, ob ich es empfehlen kann, und dabei eine Liste von Fallstricken gesammelt; überraschenderweise waren es nicht viele.Nicht null, aber ich suche ziemlich gründlich. Wenn ich erfahren kann, auf welche Fallstricke du gestoßen bist, würde ich sie gern in die Liste aufnehmen.
.pymit gesetztem Execute-Bit und der im Artikel gezeigten Shebang-Zeile.Bei Pipenv gab es Probleme, wenn eine bestimmte Abhängigkeit je nach Plattform unterschiedliche Wheels hatte; bei Poetry funktionierte das gut.
Die Lock-Datei von Pipenv enthielt nur die Abhängigkeiten der Plattform, auf der gelockt wurde, während Poetry alle Plattformvarianten in die Lock-Datei aufnimmt.
Ich habe noch keine Dokumentation dazu gefunden, wie sich
uvin diesem Fall verhält.Der Kern ist folgender:
Ein kürzlich veröffentlichter PEP definiert, wie Python-Skripte ihre Abhängigkeiten in Startkommentaren deklarieren, und uv ist ein Python-Skript-Runner und Paketmanager, der diese Abhängigkeiten scannt, erfüllt und das Skript dann so ausführt, dass die entsprechenden Module importiert werden können.
Wenn man in der Shebang, also dem Kommentar in der ersten Zeile eines Python-Skripts, die Datei mit
uvstatt mitpythonaufrufen lässt, bekommt man diesen Effekt automatisch, sobald man das Skript „ausführt“.Dadurch laufen auch Python-Skripte mit Abhängigkeiten, die eigentlich Konfiguration benötigen, einfach direkt.
$PATHsein.Und auch wenn uv die Abhängigkeiten installiert, ist nicht klar, ob es auf diese Weise auch den Python-Interpreter automatisch installiert.
Persönlich sehe ich nicht, dass das gegenüber dem alten Weg, eine Umgebung anzulegen und aufzurufen, Zeit spart.
Ich bin mir auch nicht sicher, ob uv Umgebungen nur in interaktiven Sessions laden kann oder ob es nur für die Skriptausführung verwendet wird.
Wenn du TypeScript magst, kannst du denselben Ansatz auch mit Deno nutzen. Mit Bun dürfte es vermutlich ebenfalls gehen, ausprobiert habe ich es aber nicht.
Man schreibt zum Beispiel
#! /usr/bin/env -S deno run; wenn Berechtigungs-Flags nötig sind, ergänzt man sie etwa so:#! /usr/bin/env -S deno run --allow-env --allow-read --allow-net.