Mit "#! / Usr / bin / env python" kann kein Argument an Python übergeben werden.

76

Ich brauchte ein direkt ausführbares Python-Skript, also startete ich die Datei mit #!/usr/bin/env python. Allerdings brauche ich auch ungepufferte Ausgabe, also habe ich es versucht #!/usr/bin/env python -u, aber das schlägt fehl python -u: no such file or directory.

Ich fand , dass heraus #/usr/bin/python -ufunktioniert, aber ich brauche es die zu bekommen pythonin PATHvirtuellen zu unterstützen envUmgebungen.

Was sind meine Optionen?

Eskil
quelle
1
In dieser SO-Frage finden Sie einige Informationen zur ungepufferten Ausgabe.
Mattias Nilsson

Antworten:

27

Es ist besser, Umgebungsvariablen zu verwenden, um dies zu aktivieren. Siehe Python-Dokument: http://docs.python.org/2/using/cmdline.html

für Ihren Fall:

export PYTHONUNBUFFERED=1
script.py
Larry Cai
quelle
1
Müssen Sie dies bei jedem Neustart tun?
Dan
43

In einigen Umgebungen teilt env keine Argumente auf. Also sucht deine Umgebung python -uauf deinem Weg. Wir können sh verwenden, um herumzuarbeiten. Ersetzen Sie Ihren Shebang durch die folgenden Codezeilen und alles wird gut.

#!/bin/sh
''''exec python -u -- "$0" ${1+"$@"} # '''
# vi: syntax=python

ps wir brauchen uns keine sorgen um den weg zu sh, oder?

Ade YU
quelle
2
Für diejenigen, die sich fragen, wie das funktioniert: Warum funktioniert dieses Snippet?
Martijn Pieters
14
Der ${1+"$@"}Hack war wahrscheinlich seit mindestens 20 Jahren unnötig :)
user4815162342
2
Der Hack ist vielleicht unnötig, aber er schadet nicht, oder? Es macht Spaß, davon zu wissen :-) Ich habe heute gerade davon erfahren. Wie auch immer, ich denke, es ist "exec" "python" "-u" "--" "$0" "$@"vielleicht einfacher zu verstehen - gibt es einen Fehler darin? (Ich denke, es ist nicht kompatibel mit dem 1+Hack?)
Aaron McDaid
1
Meine Methode hat einen Nachteil. Wenn Sie etwas Kompliziertes zum Bash übergeben möchten, z. B. eine Zeichenfolge mit verschachteltem 'oder ", ist Ihre Methode zuverlässiger. Es ist ein interessantes Thema! Meins ist vielleicht leichter zu verstehen, aber deins ist robuster. Vielleicht sollte Ihre Antwort klarstellen, dass sie mit beginnen muss ''''execund die Zeichenfolge # '''mit (mit einem Leerzeichen vor dem #) enden muss . Solange wir diese Regeln befolgen und keine zusätzlichen Anführungszeichen haben ''', ist Ihre Methode perfekt und flexibel.
Aaron McDaid
1
@ user4815162342 Hier ist mehr Kontext auf ${1+"$@"}. Sollte also "$@"in den meisten Fällen von alleine gut funktionieren.
Akhan
16

Wenn Sie shebang unter Linux verwenden, wird der gesamte Rest der Zeile nach dem Interpreternamen als einzelnes Argument interpretiert. Das python -uwird an übergeben, envals ob Sie Folgendes eingegeben hätten : /usr/bin/env 'python -u'. Die /usr/bin/envSuche nach einer Binärdatei namens python -u, die es keine gibt.

Jimmy Hartzell
quelle
13

Das Übergeben von Argumenten an die Shebang-Zeile ist nicht Standard und funktioniert, wie Sie experimentiert haben, unter Linux nicht in Kombination mit env. Die Lösung mit bash besteht darin, den eingebauten Befehl "set" zu verwenden, um die erforderlichen Optionen festzulegen. Ich denke, Sie können das Gleiche tun, um die ungepufferte Ausgabe von stdin mit einem Python-Befehl festzulegen.

my2c

Neuro
quelle
13

Dies mag etwas veraltet sein, aber das Handbuch env (1) sagt, dass man für diesen Fall '-S' verwenden kann

#!/usr/bin/env -S python -u

Es scheint ziemlich gut auf FreeBSD zu funktionieren.

user3249132
quelle
Wow, es wäre gut, wenn dies weit verbreitet wäre, aber auch nicht auf Cygwin verfügbar ist :(
Philwalk
6
Scheint, als ob die -SOption spezifisch für die BSD-Variante von ist, env(1)aber es ist gut zu wissen
nodakai
3
Linux hat env -Sjetzt auch - ab Coreutils 8.30 1 (es kann eine Weile dauern, bis es in einer Distribution in Ihrer Nähe erscheint). Gleiche Semantik wie bei FreeBSD env(1)- Hurra für die Portabilität guter Funktionen.
Juan
Auch auf Solaris (11.3) kein Glück ... :(
Timmah
9

Hier ist eine Skriptalternative zu /usr/bin/env, die die Übergabe von Argumenten in der Hash-Bang-Zeile ermöglicht, basierend auf /bin/bashund mit der Einschränkung, dass Leerzeichen im ausführbaren Pfad nicht zulässig sind. Ich nenne es "envns" (env No Spaces):

#!/bin/bash

ARGS=( $1 )  # separate $1 into multiple space-delimited arguments.
shift # consume $1

PROG=`which ${ARGS[0]}`
unset ARGS[0] # discard executable name

ARGS+=( "$@" ) # remainder of arguments preserved "as-is".
exec $PROG "${ARGS[@]}"

Angenommen, dieses Skript befindet sich unter / usr / local / bin / envns, dann ist hier Ihre Shebang-Zeile:

#!/usr/local/bin/envns python -u

Getestet auf Ubuntu 13.10 und Cygwin x64.

Philwalk
quelle
2
Dies sollte gebündelt werden :)
wieczorek1990
Hinweis: Die meisten Unix-ish #! Implementierungen erlauben aus Sicherheitsgründen nicht die Verwendung von Skripten. Ich bin überrascht, dass dies unter Ubunut 13.10 funktioniert hat.
Juan
funktioniert auch auf Ubuntu 16.04 ab 2019-01-20, nicht sicher über andere.
Philwalk
5

Dies ist ein Kludge und erfordert Bash, aber es funktioniert:

#!/bin/bash

python -u <(cat <<"EOF"
# Your script here
print "Hello world"
EOF
)
imgx64
quelle
1
Vielen Dank, dies funktionierte gut für mich für ein anderes Problem (mit der Mono-Csharp-Shell mit Argumenten)
IanNorton
5

Aufbauend auf Larry Cais Antwort envkönnen Sie eine Variable direkt in der Befehlszeile festlegen. Das bedeutet, dass -udies durch die entsprechende PYTHONUNBUFFEREDEinstellung vor ersetzt werden kann python:

#!/usr/bin/env PYTHONUNBUFFERED="YESSSSS" python

Funktioniert mit RHEL 6.5. Ich bin mir ziemlich sicher, dass diese Funktion envnahezu universell ist.

Verrückter Physiker
quelle
3
Zu Ihrer Information, dies funktioniert NICHT in Debian. Ich bin mir nicht sicher, warum es nicht funktioniert (um die psAusgabe zu betrachten, sollte es keinen Unterschied geben), aber es kehrt nie zurück. Es ist nicht sehr klar, ob Python selbst überhaupt läuft, wenn Sie dies in Debian tun. Ich habe dies an einigen Stellen versucht - funktioniert definitiv nicht wie erwartet im Vergleich zur entsprechenden Befehlszeile.
MartyMacGyver
@ MartinMacGyver. Sehr wahrscheinlich mit der Version von envoder sogar pythonSie verwenden.
Mad Physicist
Könnte die Version von env sein, funktioniert aber bisher mit keiner modernen Debian-Variante. Es scheint nicht, dass Python tatsächlich in diesem Szenario unter Debian ausgeführt wird, sodass es außerhalb bestimmter Plattformen und / oder Konfigurationen nur eingeschränkt verwendet werden kann.
MartyMacGyver
-1

Ich habe kürzlich einen Patch für die GNU Coreutils-Version von geschrieben env, um dieses Problem zu beheben :

http://lists.gnu.org/archive/html/coreutils/2017-05/msg00018.html

Wenn Sie dies haben, können Sie tun:

#!/usr/bin/env :lang:--foo:bar

envwird :lang:foo:--barin die Felder aufgeteilt lang, foound --bar. Es sucht PATHfür den Dolmetscher lang, und dann rufen Sie es mit Argumenten --foo, barsowie der Pfad zum Skript und das Skript Argumente.

Es gibt auch eine Funktion, mit der Sie den Namen des Skripts in der Mitte der Optionen übergeben können. Angenommen, Sie möchten ausführen lang -f <thecriptname> other-arg, gefolgt von den verbleibenden Argumenten. Mit diesem Patch envwird es folgendermaßen gemacht:

#!/usr/bin/env :lang:-f:{}:other-arg

Das Feld ganz links, das äquivalent ist, {}wird durch das erste folgende Argument ersetzt, das unter Hash-Bang-Aufruf der Skriptname ist. Dieses Argument wird dann entfernt.

Hier other-argkönnte etwas von langoder vielleicht etwas von dem Skript verarbeitet werden.

Weitere Informationen finden Sie in den zahlreichen echoTestfällen im Patch.

Ich habe das :Zeichen gewählt, weil es ein vorhandenes Trennzeichen ist, das PATHauf POSIX-Systemen verwendet wird. Da envdie PATHSuche durchgeführt wird, ist es offensichtlich unwahrscheinlich, dass sie für ein Programm verwendet wird, dessen Name einen Doppelpunkt enthält. Die {}Markierung stammt vom findDienstprogramm, mit dem das Einfügen eines Pfads in die -execBefehlszeile bezeichnet wird.

Kaz
quelle
1
Für jeden, der dies liest und sich hoffnungsvoll fühlt, wurde der Patch aufgegeben .
Alex Miller