So öffnen Sie zuverlässig eine Datei im selben Verzeichnis wie ein Python-Skript

157

Ich habe früher Dateien geöffnet, die sich im selben Verzeichnis wie das aktuell ausgeführte Python-Skript befanden, indem ich einfach einen Befehl wie verwendet habe

open("Some file.txt", "r")

Ich stellte jedoch fest, dass beim Ausführen des Skripts in Windows durch Doppelklicken versucht wurde, die Datei aus dem falschen Verzeichnis zu öffnen.

Seitdem habe ich einen Befehl des Formulars verwendet

open(os.path.join(sys.path[0], "Some file.txt"), "r")

wann immer ich eine Datei öffnen wollte. Dies funktioniert für meine spezielle Verwendung, aber ich bin nicht sicher, ob sys.path[0]es in einem anderen Anwendungsfall fehlschlagen könnte.

Meine Frage lautet also: Was ist der beste und zuverlässigste Weg, um eine Datei zu öffnen, die sich im selben Verzeichnis befindet wie das aktuell ausgeführte Python-Skript?

Folgendes konnte ich bisher herausfinden:

  • os.getcwd()und os.path.abspath('')geben Sie das "aktuelle Arbeitsverzeichnis" zurück, nicht das Skriptverzeichnis.

  • os.path.dirname(sys.argv[0])und os.path.dirname(__file__)geben Sie den Pfad zurück, der zum Aufrufen des Skripts verwendet wird. Dieser kann relativ oder sogar leer sein (wenn sich das Skript im cwd befindet). Auch __file__gibt es nicht , wenn das Skript in IDLE oder PythonWin ausgeführt wird.

  • sys.path[0]und os.path.abspath(os.path.dirname(sys.argv[0]))scheinen das Skriptverzeichnis zurückzugeben. Ich bin mir nicht sicher, ob es einen Unterschied zwischen diesen beiden gibt.

Bearbeiten:

Ich habe gerade festgestellt, dass das, was ich tun möchte, besser als "Öffnen einer Datei im selben Verzeichnis wie das enthaltende Modul" beschrieben werden kann. Mit anderen Worten, wenn ich ein Modul importiere, das ich geschrieben habe und das sich in einem anderen Verzeichnis befindet, und dieses Modul eine Datei öffnet, möchte ich, dass es nach der Datei im Verzeichnis des Moduls sucht. Ich glaube nicht, dass irgendetwas, was ich gefunden habe, dazu in der Lage ist ...

dln385
quelle

Antworten:

199

Ich benutze immer:

__location__ = os.path.realpath(
    os.path.join(os.getcwd(), os.path.dirname(__file__)))

Der join()Aufruf stellt das aktuelle Arbeitsverzeichnis voran, aber die Dokumentation besagt, dass, wenn ein Pfad absolut ist, alle anderen verbleibenden Pfade gelöscht werden. Daher getcwd()wird fallen gelassen, wenndirname(__file__) ein absoluter Pfad zurückgegeben wird.

Außerdem werden durch den realpathAufruf symbolische Links aufgelöst, falls vorhanden. Dies vermeidet Probleme bei der Bereitstellung mit setuptools auf Linux-Systemen (Skripte sind mit verknüpft/usr/bin/ - zumindest unter Debian - .

Sie können Folgendes verwenden, um Dateien im selben Ordner zu öffnen:

f = open(os.path.join(__location__, 'bundled-resource.jpg'));
# ...

Ich verwende dies, um Ressourcen mit mehreren Django-Anwendungen unter Windows und Linux zu bündeln, und es funktioniert wie ein Zauber!

André Caron
quelle
4
Wenn __file__nicht verwendet werden kann, verwenden Sie sys.argv[0]anstelle von dirname(__file__). Der Rest sollte wie erwartet funktionieren. Ich verwende es gerne, __file__weil es im Bibliothekscode sys.argv[0]möglicherweise überhaupt nicht auf Ihren Code verweist, insbesondere wenn es über ein Skript eines Drittanbieters importiert wird.
André Caron
1
Das Problem dabei ist, dass es unterschiedlich ist, wenn die ausgeführte Datei direkt vom Unterbrecher stammt oder importiert wird. Siehe meine Antwort für die Unterschiede zwischen Datei und sys.argv [0]
Zimm3r
Ist es also richtig zu sagen, dass die in der Antwort von Zimm3r beschriebene Variation realpath( join( getcwd(), dirname(__file__) ))wie hier beschrieben behandelt wird?
PianoJames
44

So zitieren Sie aus der Python-Dokumentation:

Wie beim Programmstart initialisiert, ist das erste Element dieser Liste, Pfad [0], das Verzeichnis, das das Skript enthält, mit dem der Python-Interpreter aufgerufen wurde. Wenn das Skriptverzeichnis nicht verfügbar ist (z. B. wenn der Interpreter interaktiv aufgerufen wird oder wenn das Skript von der Standardeingabe gelesen wird), ist Pfad [0] die leere Zeichenfolge, die Python anweist, zuerst Module im aktuellen Verzeichnis zu suchen. Beachten Sie, dass das Skriptverzeichnis vor den Einträgen eingefügt wird, die als Ergebnis von PYTHONPATH eingefügt wurden.

sys.path [0] ist das, wonach Sie suchen.

ROTER AFFE
quelle
10
Und für den vollständigen Pfad der Datei : os.path.join(sys.path[0], 'some file.txt'). Das sollte Leerzeichen und Schrägstriche auf allen Systemen korrekt behandeln.
Jacktose
Diese Antwort auf die erste Frage, nicht die nach der Bearbeitung.
mcoolive
22

Ok hier ist was ich mache

sys.argv ist immer das, was Sie in das Terminal eingeben oder als Dateipfad verwenden, wenn Sie es mit python.exe oder pythonw.exe ausführen

Sie können die Datei text.py beispielsweise auf verschiedene Arten ausführen. Sie geben Ihnen jeweils eine andere Antwort. Sie geben Ihnen immer den Pfad, in den Python eingegeben wurde.

    C:\Documents and Settings\Admin>python test.py
    sys.argv[0]: test.py
    C:\Documents and Settings\Admin>python "C:\Documents and Settings\Admin\test.py"
    sys.argv[0]: C:\Documents and Settings\Admin\test.py

Ok, wissen Sie also, dass Sie den Dateinamen erhalten können, eine große Sache. Um das Anwendungsverzeichnis zu erhalten, können Sie os.path verwenden, insbesondere abspath und dirname

    import sys, os
    print os.path.dirname(os.path.abspath(sys.argv[0]))

Das wird dies ausgeben:

   C:\Documents and Settings\Admin\

Dies wird immer ausgegeben, unabhängig davon, ob Sie python test.py oder python "C: \ Dokumente und Einstellungen \ Admin \ test.py" eingeben.

Das Problem bei der Verwendung von __file__ Betrachten Sie diese beiden Dateien test.py

import sys
import os

def paths():
        print "__file__: %s" % __file__
        print "sys.argv: %s" % sys.argv[0]

        a_f = os.path.abspath(__file__)
        a_s = os.path.abspath(sys.argv[0])

        print "abs __file__: %s" % a_f
        print "abs sys.argv: %s" % a_s

if __name__ == "__main__":
    paths()

import_test.py

import test
import sys

test.paths()

print "--------"
print __file__
print sys.argv[0]

Ausgabe von "python test.py"

C:\Documents and Settings\Admin>python test.py
__file__: test.py
sys.argv: test.py
abs __file__: C:\Documents and Settings\Admin\test.py
abs sys.argv: C:\Documents and Settings\Admin\test.py

Ausgabe von "python test_import.py"

C:\Documents and Settings\Admin>python test_import.py
__file__: C:\Documents and Settings\Admin\test.pyc
sys.argv: test_import.py
abs __file__: C:\Documents and Settings\Admin\test.pyc
abs sys.argv: C:\Documents and Settings\Admin\test_import.py
--------
test_import.py
test_import.py

Wie Sie sehen können , gibt Ihnen file immer die Python-Datei, aus der sie ausgeführt wird, und sys.argv [0] gibt Ihnen die Datei an, die Sie immer vom Interpreter ausgeführt haben. Abhängig von Ihren Bedürfnissen müssen Sie auswählen, welches am besten zu Ihren Bedürfnissen passt.

Zimm3r
quelle
3
Dies ist ein ausführlicher Beweis dafür, dass die Implementierung die Dokumentation widerspiegelt. __file__ist angeblich „geben Sie immer den Pfad der aktuellen Datei“, und sys.argv[0]ist angeblich auf „immer den Pfad des Skripts geben, die den Prozess eingeleitet“. In jedem Fall __file__liefert die Verwendung des aufgerufenen Skripts immer genaue Ergebnisse.
André Caron
Wenn Sie den Verweis __file__auf die oberste Ebene des Skripts haben, funktioniert dies wie erwartet.
Matthew Schinckel
-1

Ich konnte den von dcolish bereitgestellten Code erfolgreich verwenden, da ich ein ähnliches Problem beim Lesen einer bestimmten Textdatei hatte. Die Datei befindet sich nicht im selben CWD wie die Python-Datei.

Jaclyn Horton
quelle
1
Bitte fügen Sie als Antwort kein "Danke" hinzu. Sobald Sie einen ausreichenden Ruf haben , können Sie Fragen und Antworten abstimmen , die Sie als hilfreich empfunden haben. - Aus dem Rückblick
Roberto Caboni
-3

Ich würde es so machen:

from os.path import abspath, exists

f_path = abspath("fooabar.txt")

if exists(f_path):
    with open(f_path) as f:
        print f.read()

Der obige Code erstellt mit abspath einen absoluten Pfad zur Datei und entspricht der Verwendung von normpath(join(os.getcwd(), path))[das stammt aus den Pydocs]. Anschließend wird überprüft, ob diese Datei tatsächlich vorhanden ist, und sie wird dann mithilfe eines Kontextmanagers geöffnet, sodass Sie nicht daran denken müssen, close für das Dateihandle aufzurufen. IMHO, wenn Sie es so machen, werden Sie auf lange Sicht viel Schmerz sparen.

dcolish
quelle
Dies beantwortet nicht die Frage des Posters. dln385 sagte ausdrücklich, dass os.path.abspathPfade zu Dateien im selben Ordner wie das Skript nicht aufgelöst werden, wenn sich das Skript nicht im aktuellen Verzeichnis befindet.
André Caron
AH! Ich nahm an, dass der Benutzer dieses Skript im selben Verzeichnis wie die Datei ausführte, die er lesen wollte, NICHT im Modulverzeichnis von etwas in seinem PYTHONPATH. Das wird mich lehren, Annahmen zu treffen ...
dcolish
abspath funktioniert nicht, da die Python-Laufzeit mit dieser Funktion nicht im Betriebssystem-Dateisystem suchen kann.
Akshat Thakar