364 Stimmen

Wie bestimmen Sie das aktuelle Skriptverzeichnis richtig?

Ich möchte sehen, was ist der beste Weg, um das aktuelle Skript-Verzeichnis in Python zu bestimmen.

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 mit exec , execfile
  • __module__ ist 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 ein anderes aktuelles Verzeichnis haben kann.

Ich weiß, dass es keine perfekte Lösung gibt, aber ich bin auf der 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 das funktioniert nicht, wenn Sie das Skript von einem anderen Skript aus ausführen mit exec() .

Warnung

Jede Lösung, die das aktuelle Verzeichnis verwendet, wird fehlschlagen. Dieses kann je nach Art des Skriptaufrufs unterschiedlich sein oder innerhalb des laufenden Skripts geändert werden.

7voto

Will McCutchen Punkte 12907

Würde

import os
cwd = os.getcwd()

tun, was Sie wollen? Ich bin mir nicht sicher, was genau Sie mit dem "aktuellen Skriptverzeichnis" meinen. Was würde die erwartete Ausgabe für die von Ihnen genannten Anwendungsfälle sein?

5voto

wim Punkte 297608

Verwenden Sie einfach os.path.dirname(os.path.abspath(__file__)) und sehr sorgfältig zu prüfen, ob ein tatsächlicher Bedarf für den Fall besteht, dass exec verwendet wird. Wenn Sie Ihr Skript nicht als Modul verwenden können, könnte das ein Zeichen für ein fehlerhaftes Design sein.

Bitte beachten Sie Zen der Python #8 und wenn Sie der Meinung sind, dass es ein gutes Argument für einen Anwendungsfall gibt, bei dem es funktionieren muss für exec dann teilen Sie uns bitte weitere Einzelheiten über den Hintergrund des Problems mit.

5voto

Simon Streicher Punkte 2358

Hinweis: Diese Antwort ist jetzt ein Paket

https://github.com/heetbeet/locate

$ pip install locate

$ python
>>> from locate import this_dir
>>> print(this_dir())
C:/Users/simon

Para .py Skripte sowie die interaktive Nutzung:

Ich verwende häufig das Verzeichnis meiner Skripte (um auf die darin gespeicherten Dateien zuzugreifen), aber ich führe diese Skripte auch häufig in einer interaktiven Shell aus, um sie zu debuggen. Ich definiere this_dir als:

  • Beim Ausführen oder Importieren einer .py Datei, das Basisverzeichnis der Datei. Dies ist immer der richtige Pfad.
  • Bei der Durchführung einer .ipyn notebook, das aktuelle Arbeitsverzeichnis. Dies ist immer der richtige Pfad, da Jupyter das Arbeitsverzeichnis als .ipynb Basisverzeichnis.
  • Bei der Ausführung in einer REPL, das aktuelle Arbeitsverzeichnis. Hmm, was ist der tatsächliche "richtige Pfad", wenn der Code von einer Datei losgelöst ist? Wechseln Sie lieber in den "richtigen Pfad", bevor Sie die REPL aufrufen.

Python 3.4 (und höher):

from pathlib import Path
this_dir = Path(globals().get("__file__", "./_")).absolute().parent

Python 2 (und höher):

import os
this_dir = os.path.dirname(os.path.abspath(globals().get("__file__", "./_")))

Erläuterung:

  • globals() gibt alle globalen Variablen in Form eines Wörterbuchs zurück.
  • .get("__file__", "./_") gibt den Wert des Schlüssels "__file__" wenn sie existiert in globals() , andernfalls wird der angegebene Standardwert zurückgegeben "./_" .
  • Der Rest des Codes wird einfach erweitert __file__ (oder "./_" ) in einen absoluten Dateipfad und gibt dann das Basisverzeichnis des Dateipfads zurück.

3voto

synthesizerpatel Punkte 26081

Erstens.. ein paar fehlende Anwendungsfälle hier, wenn wir über Möglichkeiten, anonymen Code zu injizieren sprechen

code.compile_command()
code.interact()
imp.load_compiled()
imp.load_dynamic()
imp.load_module()
__builtin__.compile()
loading C compiled shared objects? example: _socket?)

Aber die eigentliche Frage ist, was ist Ihr Ziel - versuchen Sie, eine Art von Sicherheit zu erzwingen? Oder sind Sie nur daran interessiert, was geladen wird.

Wenn Sie interessiert sind an Sicherheit ist der Dateiname, der über exec/execfile importiert wird, unerheblich - Sie sollten rexec die Folgendes bietet:

Dieses Modul enthält die die r_eval(), r_execfile() unterstützt, r_exec() und r_import() unterstützt, die sind eingeschränkte Versionen der Standard Python-Funktionen eval(), execfile() und die Anweisungen exec und import. Code der in dieser eingeschränkten Umgebung ausgeführt wird ausgeführt wird, hat nur Zugriff auf Module und Funktionen, die als sicher eingestuft sind; Sie können Unterklasse RExec ad nach Belieben.

Wenn es sich jedoch eher um eine akademische Beschäftigung handelt, finden Sie hier ein paar verrückte Ansätze, die Sie die Sie vielleicht ein wenig vertiefen können

Beispiel-Skripte:

./deep.py

print ' >> level 1'
execfile('deeper.py')
print ' << level 1'

./deeper.py

print '\t >> level 2'
exec("import sys; sys.path.append('/tmp'); import deepest")
print '\t << level 2'

/tmp/deepest.py

print '\t\t >> level 3'
print '\t\t\t I can see the earths core.'
print '\t\t << level 3'

./codespy.py

import sys, os

def overseer(frame, event, arg):
    print "loaded(%s)" % os.path.abspath(frame.f_code.co_filename)

sys.settrace(overseer)
execfile("deep.py")
sys.exit(0)

Ausgabe

loaded(/Users/synthesizerpatel/deep.py)
>> level 1
loaded(/Users/synthesizerpatel/deeper.py)
    >> level 2
loaded(/Users/synthesizerpatel/<string>)
loaded(/tmp/deepest.py)
        >> level 3
            I can see the earths core.
        << level 3
    << level 2
<< level 1

Natürlich ist dies eine ressourcenintensive Methode, denn Sie würden Ihren gesamten Code. Nicht sehr effizient. Aber, ich denke, es ist ein neuer Ansatz da er auch dann noch funktioniert, wenn man tiefer in das Nest eindringt. Man kann 'eval' nicht außer Kraft setzen. Obwohl Sie kann Überschreiben Sie execfile().

Beachten Sie, dass dieser Ansatz nur exec/execfile abdeckt, nicht "import". Für 'Modul'-Load Hooking auf höherer Ebene können Sie vielleicht verwenden sys.path_hooks (Bericht mit freundlicher Genehmigung von PyMOTW).

Das ist alles, was ich aus dem Stegreif weiß.

2voto

sorin Punkte 147675

Hier ist eine Teillösung, die immer noch besser ist als alle bisher veröffentlichten Lösungen.

import sys, os, os.path, inspect

#os.chdir("..")

if '__file__' not in locals():
    __file__ = inspect.getframeinfo(inspect.currentframe())[0]

print os.path.dirname(os.path.abspath(__file__))

Dies funktioniert nun bei allen Anrufen, aber wenn jemand die chdir() um das aktuelle Verzeichnis zu wechseln, wird dies ebenfalls fehlschlagen.

Anmerkungen:

  • sys.argv[0] wird nicht funktionieren, wird zurückkehren -c wenn Sie das Skript mit python -c "execfile('path-tester.py')"
  • Ich habe einen vollständigen Test veröffentlicht unter https://gist.github.com/1385555 und Sie können sie gerne verbessern.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X