Ich würde gerne sehen, wie man das aktuelle Skriptverzeichnis in Python am besten ermittelt.
Ich habe festgestellt, dass es aufgrund der vielen Möglichkeiten, Python-Code aufzurufen, schwierig ist, eine gute Lösung zu finden.
Hier sind einige Probleme:
__file__
ist nicht definiert, wenn das Skript ausgeführt wird mitexec
,execfile
__module__
wird nur in Modulen definiert
Anwendungsfälle:
./myfile.py
python myfile.py
./somedir/myfile.py
python somedir/myfile.py
execfile('myfile.py')
(von einem anderen Skript, das sich in einem anderen Verzeichnis befinden kann und das ein anderes aktuelles Verzeichnis haben kann.
Ich weiß, dass es keine perfekte Lösung gibt, aber ich suche nach dem besten Ansatz, der die meisten Fälle löst.
Der am häufigsten verwendete Ansatz ist, os.path.dirname(os.path.abspath(__file__))
aber dies funktioniert wirklich nicht, wenn Sie das Skript von einem anderen mit ausführen exec()
.
Warnung
Jede Lösung, die das aktuelle Verzeichnis verwendet, schlägt fehl. Dies kann je nach Aufruf des Skripts unterschiedlich sein oder innerhalb des laufenden Skripts geändert werden.
python
pythonpath
dirname
Bogdan
quelle
quelle
pathlib
Lösung, wenn Sie Python 3.4 oder höher verwenden: stackoverflow.com/a/48931294/1011724python myfile.py
von einer Shell aus arbeiten, funktioniert es, aber beide:!python %
und:!python myfile.py
innerhalb von vim schlagen fehl mit Das System kann den angegebenen Pfad nicht finden. Das ist ziemlich nervig. Kann jemand den Grund dafür und mögliche Problemumgehungen kommentieren?Antworten:
ist in der Tat das Beste, was Sie bekommen werden.
Es ist ungewöhnlich, ein Skript mit
exec
/ auszuführenexecfile
. Normalerweise sollten Sie die Modulinfrastruktur zum Laden von Skripten verwenden. Wenn Sie diese Methoden verwenden müssen, empfehle ich die Einstellung__file__
in derglobals
Übergabe an das Skript, damit es diesen Dateinamen lesen kann.Es gibt keine andere Möglichkeit, den Dateinamen im ausgeführten Code abzurufen: Wie Sie bemerken, befindet sich das CWD möglicherweise an einer völlig anderen Stelle.
quelle
Wenn Sie wirklich den Fall abdecken möchten, über den ein Skript aufgerufen wird
execfile(...)
, können Sie mithilfe desinspect
Moduls den Dateinamen (einschließlich des Pfads) ableiten. Soweit mir bekannt ist, funktioniert dies in allen von Ihnen aufgeführten Fällen:quelle
chdir()
vor der Funktion aufzurufen . Dadurch wird das Ergebnis geändert. Auch das Aufrufen des Python-Skripts aus einem anderen Verzeichnis ändert das Ergebnis, sodass es keine gute Lösung ist.os.path.expanduser("~")
ist eine plattformübergreifende Methode, um das Benutzerverzeichnis abzurufen. Leider ist dies nicht die bewährte Methode von Windows, um Anwendungsdaten zu speichern.chdir()
bevor ich das Skript ausgeführt habe. es erzeugt ein korrektes Ergebnis. Ich habe versucht, das Skript aus einem anderen Verzeichnis aufzurufen, und es funktioniert auch. Die Ergebnisse sind die gleichen wie bei einerinspect.getabsfile()
Lösung auf Basis .Es funktioniert auf CPython, Jython, Pypy. Es funktioniert, wenn das Skript mit ausgeführt wird
execfile()
(sys.argv[0]
und__file__
-basierte Lösungen hier fehlschlagen würden). Es funktioniert, wenn sich das Skript in einer ausführbaren Zip-Datei (/ einem Ei) befindet . Es funktioniert, wenn das SkriptPYTHONPATH=/path/to/library.zip python -mscript_to_run
aus einer Zip-Datei "importiert" ( ) wird. In diesem Fall wird der Archivpfad zurückgegeben. Es funktioniert, wenn das Skript in eine eigenständige ausführbare Datei (sys.frozen
) kompiliert wird . Es funktioniert für Symlinks (realpath
eliminiert symbolische Links). Es funktioniert in einem interaktiven Interpreter. In diesem Fall wird das aktuelle Arbeitsverzeichnis zurückgegeben.quelle
getabsfile(..)
in der Dokumentation fürinspect
nicht erwähnt wird ? Es wird in der Quelle angezeigt, die von dieser Seite verlinkt ist.getsourcefile()
,getfile()
der dokumentiert wird.In Python 3.4+ können Sie das einfachere
pathlib
Modul verwenden:quelle
Path(__file__)
(keine Notwendigkeit für dasinspect
Modul).Path(__file__)
gibt dies an,/path/to/script/currentscript.py
wann das OP es erhalten wollte/path/to/script/
parent = Path(__file__).resolve().parent
Das ist viel schöner..joinpath()
(oder den/
Operator) dafür verwenden, nicht+
.Der
os.path...
Ansatz war das "erledigte Ding" in Python 2.In Python 3 finden Sie das Skriptverzeichnis wie folgt:
quelle
Path(__file__).parent
. Abercwd
ist eine Fehlbezeichnung, das ist nicht das aktuelle Arbeitsverzeichnis , sondern das Verzeichnis der Datei . Sie könnten gleich sein, aber das ist normalerweise nicht der Fall.Verwenden Sie einfach
os.path.dirname(os.path.abspath(__file__))
und prüfen Sie sehr sorgfältig, ob für den Fall, in demexec
es verwendet wird, ein wirklicher Bedarf besteht. Es könnte ein Zeichen für ein problematisches Design sein, wenn Sie Ihr Skript nicht als Modul verwenden können.Denken Sie an Zen of Python # 8 , und wenn Sie der Meinung sind, dass es ein gutes Argument für einen Anwendungsfall gibt, für den es funktionieren muss
exec
, teilen Sie uns bitte einige Details zum Hintergrund des Problems mit.quelle
Würde
Tun Sie, was Sie wollen? Ich bin mir nicht sicher, was genau Sie mit dem "aktuellen Skriptverzeichnis" meinen. Was wäre die erwartete Ausgabe für die von Ihnen angegebenen Anwendungsfälle?
quelle
exec('myfile.py')
, genau wie__file__
undsys.argv[0]
.Erstens ... ein paar fehlende Anwendungsfälle hier, wenn wir über Möglichkeiten sprechen, anonymen Code einzufügen.
Die eigentliche Frage ist jedoch, was ist Ihr Ziel - versuchen Sie, irgendeine Art von Sicherheit durchzusetzen? Oder sind Sie nur daran interessiert, was geladen wird?
Wenn Sie an Sicherheit interessiert sind , spielt der Dateiname, der über exec / execfile importiert wird, keine Rolle. Sie sollten rexec verwenden , das Folgendes bietet:
Wenn dies jedoch eher eine akademische Angelegenheit ist, finden Sie hier einige doofe Ansätze, in die Sie möglicherweise etwas tiefer eintauchen können.
Beispielskripte:
./deep.py
./deeper.py
/tmp/deepest.py
./codespy.py
Ausgabe
Dies ist natürlich eine ressourcenintensive Methode. Sie würden Ihren gesamten Code nachverfolgen. Nicht sehr effizient. Aber ich denke, es ist ein neuartiger Ansatz, da er auch dann weiter funktioniert, wenn Sie tiefer in das Nest eindringen. Sie können 'eval' nicht überschreiben. Obwohl Sie können execfile außer Kraft setzen ().
Beachten Sie, dass dieser Ansatz nur exec / execfile abdeckt, nicht 'import'. Für das Laden von Modulen auf höherer Ebene können Sie möglicherweise sys.path_hooks verwenden (mit freundlicher Genehmigung von PyMOTW).
Das ist alles, was ich auf dem Kopf habe.
quelle
Hier ist eine Teillösung, die immer noch besser ist als alle bisher veröffentlichten.
Jetzt funktioniert dies bei allen Aufrufen, aber wenn jemand
chdir()
das aktuelle Verzeichnis ändert, schlägt dies ebenfalls fehl.Anmerkungen:
sys.argv[0]
wird nicht funktionieren, wird zurückkehren,-c
wenn Sie das Skript mit ausführenpython -c "execfile('path-tester.py')"
quelle
Dies sollte in den meisten Fällen funktionieren:
quelle
Hoffentlich hilft dies: - Wenn Sie ein Skript / Modul von einem beliebigen Ort aus ausführen, können Sie auf die
__file__
Variable zugreifen , bei der es sich um eine Modulvariable handelt, die den Speicherort des Skripts darstellt.Wenn Sie dagegen den Interpreter verwenden, haben Sie keinen Zugriff auf diese Variable. Dort erhalten Sie einen Namen
NameError
und erhaltenos.getcwd()
das falsche Verzeichnis, wenn Sie die Datei von einem anderen Ort aus ausführen.Diese Lösung sollte Ihnen in allen Fällen das bieten, wonach Sie suchen:
Ich habe es nicht gründlich getestet, aber es hat mein Problem gelöst.
quelle