Python os.path.join unter Windows

96

Ich versuche Python zu lernen und mache ein Programm, das ein Skript ausgibt. Ich möchte os.path.join verwenden, bin aber ziemlich verwirrt. Laut den Dokumenten, wenn ich sage:

os.path.join('c:', 'sourcedir')

Ich verstehe "C:sourcedir". Laut den Dokumenten ist das normal, oder?

Wenn ich den Befehl copytree verwende, gibt Python ihn auf die gewünschte Weise aus, zum Beispiel:

import shutil
src = os.path.join('c:', 'src')
dst = os.path.join('c:', 'dst')
shutil.copytree(src, dst)

Hier ist der Fehlercode, den ich bekomme:

WindowsError: [Fehler 3] Das System kann den angegebenen Pfad nicht finden: 'C: src /*.*'

Wenn ich das os.path.joinmit os.path.normpatheinpacke, bekomme ich den gleichen Fehler.

Wenn dies os.path.joinnicht auf diese Weise verwendet werden kann, bin ich hinsichtlich seines Zwecks verwirrt.

Laut den von Stack Overflow vorgeschlagenen Seiten sollten beim Join keine Schrägstriche verwendet werden - das ist richtig, nehme ich an?

Frank E.
quelle

Antworten:

59

Windows hat ein Konzept des aktuellen Verzeichnisses für jedes Laufwerk. Aus diesem Grund "c:sourcedir"bedeutet "Sourcedir" im aktuellen C: -Verzeichnis, und Sie müssen ein absolutes Verzeichnis angeben.

Alle diese Funktionen sollten funktionieren und das gleiche Ergebnis liefern, aber ich habe derzeit keine Windows-VM gestartet, um dies zu überprüfen:

"c:/sourcedir"
os.path.join("/", "c:", "sourcedir")
os.path.join("c:/", "sourcedir")

quelle
8
os.path.join ('C: /', 'sourcedir') funktionierte wie erwartet. Ich danke Ihnen sehr gut, Sir :) Die anderen '//' 'c:' 'c: \\' haben nicht funktioniert (C: \\ hat zwei Backslashes erstellt, C: \ hat überhaupt nicht funktioniert) Nochmals vielen Dank ghostdog74 , Smashery und Roger Pate. Ich stehe in deiner Schuld :)
Frank E.
Entschuldigung, Zeilenumbrüche wurden nicht im Kommentar festgehalten, es sieht sehr chaotisch aus
Frank E.
Auch wenn dies in einigen Fällen funktioniert, ist die Antwort von @AndreasT eine viel bessere Lösung. Bei Verwendung von os.sep wird je nach Betriebssystem zwischen / und \ gewählt.
SenhorLucas
Gibt es irgendeinen Grund, etwas zu verwenden os.path.joinoder os.sepob Sie es c:trotzdem angeben wollen? c:macht auf anderen Betriebssystemen keinen Sinn.
naught101
Alle diese Lösungen sind nur teilweise zufriedenstellend. Es ist in Ordnung, das Trennzeichen manuell hinzuzufügen, wenn Sie einen bestimmten Fall haben. Wenn Sie dies jedoch programmgesteuert tun möchten, nach welchen Kriterien os.path.join('c:','folder')funktioniert dies anders als os.path.join('folder','file')? Liegt es an der: oder weil 'c: `ein Laufwerk ist?
Vincenzooo
121

Um noch pedantischer zu sein, wäre die konsistenteste Antwort von Python Doc:

mypath = os.path.join('c:', os.sep, 'sourcedir')

Da Sie auch os.sep für den Posix-Root-Pfad benötigen:

mypath = os.path.join(os.sep, 'usr', 'lib')
AndreasT
quelle
4
Entschuldigen Sie meine Unwissenheit - Es sieht so aus, als ob der Code zwischen Windows und Linux immer noch variiert. Was macht also os.sepüberlegen?
PianoJames
3
Bitte beachten Sie dieses Snafu, wenn Sie versuchen zu injizieren os.sep. Es funktioniert nur nach dem bloßen Laufwerksbuchstaben. >>> os.path.join ("C: \ goodbye", os.sep, "temp") 'C: \\ temp'
Jobu
1
@pianoJames Meine Antwort baut auf dieser auf und bietet eine systemunabhängige Lösung: stackoverflow.com/a/51276165/3996580
Scott Gigante
Ich verstehe den Sinn all dieser "pedantischen" Lösungen nicht. os.sepist nützlich, wenn Sie Pfade bearbeiten möchten, ohne Annahmen über das Trennzeichen zu treffen. Es ist sinnlos, es zu verwenden, os.path.join()da es bereits das richtige Trennzeichen kennt. Es ist auch sinnlos, wenn Sie das Stammverzeichnis explizit nach Namen angeben müssen (wie Sie in Ihrem eigenen Beispiel sehen können). Warum "c:" + os.sepstatt einfach "c:\\"oder os.sep + "usr"statt einfach "/usr"? Beachten Sie auch, dass Sie in Win-Shells nicht cd c:können cd c:\ , aber Sie können , was darauf hindeutet, dass der Stammname tatsächlich lautet c:\ .
Michael Ekoka
13

Der Grund dafür, os.path.join('C:', 'src')dass Sie nicht wie erwartet funktionieren, liegt in der Dokumentation, auf die Sie verlinkt haben:

Beachten Sie, dass unter Windows os.path.join ("c:", "foo") einen Pfad relativ zum aktuellen Verzeichnis auf Laufwerk C: (c: foo) darstellt, nicht c : \ foo.

Wie Ghostdog sagte, möchten Sie wahrscheinlich mypath=os.path.join('c:\\', 'sourcedir')

Smashery
quelle
11

Um pedantisch zu sein, ist es wahrscheinlich nicht gut, entweder / oder \ als Pfadtrennzeichen fest zu codieren. Vielleicht wäre das am besten?

mypath = os.path.join('c:%s' % os.sep, 'sourcedir')

oder

mypath = os.path.join('c:' + os.sep, 'sourcedir')
Matt Ball
quelle
11

Für eine systemunabhängige Lösung, die sowohl unter Windows als auch unter Linux funktioniert, unabhängig vom Eingabepfad, könnte man sie verwenden os.path.join(os.sep, rootdir + os.sep, targetdir)

Auf Fenstern:

>>> os.path.join(os.sep, "C:" + os.sep, "Windows")
'C:\\Windows'

Unter Linux:

>>> os.path.join(os.sep, "usr" + os.sep, "lib")
'/usr/lib'
Scott Gigante
quelle
1
Vielen Dank! Dies ist umso nützlicher, als es nicht unter dem zuvor erwähnten Gotcha @Jobu leidet: os.path.join (os.sep, "C: \\ a" + os.sep, "b") gibt "C:" zurück. \\ a \\ b "unter Windows.
PianoJames
1
Wie ist eines dieser Beispiele systemunabhängig? c:existiert nicht auf * nix und usrexistiert nicht unter Windows ..
naught101
Der Funktionsaufruf os.path.join(os.sep, rootdir + os.sep, targetdir)ist systemunabhängig, gerade weil er mit diesen beiden systemspezifischen Beispielen funktioniert, ohne dass der Code geändert werden muss.
Scott Gigante
Diese Lösung basiert, ähnlich wie der frühere Beitrag, der sie inspiriert hat, immer noch darauf, rootdir wie zu setzen rootdir = "usr" if nix else "c:". Aber das direktere und genauere rootdir = "/usr" if nix else "c:\\"funktioniert genauso gut, ohne die os.sepAkrobatik und das daraus resultierende Kopfkratzen. Es besteht keine Gefahr , dass ein Root - Verzeichnis auf * nichts mit etwas anderes als ein Schrägstrich beginnen wird, oder dass Windows Stammverzeichnisse ohne Hinter Kolon und Backslash (zB in Win Schalen genannt haben, können Sie nicht einfach tun cd c:, würden Sie müssen den nachfolgenden Backslash angeben), warum also etwas anderes vorgeben?
Michael Ekoka
7

Ich würde sagen, dies ist ein (Windows-) Python-Fehler.

Warum Fehler?

Ich denke, diese Aussage sollte sein True

os.path.join(*os.path.dirname(os.path.abspath(__file__)).split(os.path.sep))==os.path.dirname(os.path.abspath(__file__))

Aber es ist Falseauf Windows-Maschinen.

georg
quelle
1
Ich bin geneigt zuzustimmen, dass dies einen Python-Fehler darstellt. Ist das noch der Fall? ( Geschrieben aus der glorreichen utopischen Zukunft von Ende 2015. )
Cecil Curry
Ich kann diese Frage in Bezug auf Windows nicht beantworten, da ich keinen Zugriff auf einen Windows-Computer habe, aber ich denke, das Verhalten von Python in Bezug auf diese Frage hat sich nicht geändert. Auf jeden Fall gilt diese Anweisung auch nicht für Linux-Implementierungen, da die erste Anweisung den Pfad ohne das führende Trennzeichen (auch als Stammverzeichnis bezeichnet) zurückgibt, während die zweite Anweisung den Pfad einschließlich des führenden Trennzeichens zurückgibt.
Georg
Meine Antwort auf diese Frage gefällt mir eigentlich nicht mehr. Aber ich mag auch Pythons Verhalten in dieser Hinsicht nicht.
Georg
@ Cecil Ich bin gerade wegen des gleichen Problems auf dieser Frage ... es scheint immer noch der Fall zu sein.
Joshmcode
5

Versuchen Sie es mit einem Windows-Pfad

mypath=os.path.join('c:\\', 'sourcedir')

Grundsätzlich müssen Sie dem Schrägstrich entkommen

Ghostdog74
quelle
4

Sie haben einige mögliche Ansätze, um Pfade unter Windows zu behandeln, von den am festesten codierten (wie die Verwendung von rohen String-Literalen oder das Entkommen von Backslashes) bis zu den kleinsten. Hier einige Beispiele, die wie erwartet funktionieren. Verwenden Sie, was Ihren Anforderungen besser entspricht.

In[1]: from os.path import join, isdir

In[2]: from os import sep

In[3]: isdir(join("c:", "\\", "Users"))
Out[3]: True

In[4]: isdir(join("c:", "/", "Users"))
Out[4]: True

In[5]: isdir(join("c:", sep, "Users"))
Out[5]: True
Marco Gomez
quelle
0

Zustimmung zu @ georg-

Ich würde dann sagen, warum wir lahm brauchen os.path.join- besser zu benutzen str.joinoder unicode.joinzB

sys.path.append('{0}'.join(os.path.dirname(__file__).split(os.path.sep)[0:-1]).format(os.path.sep))
Islam
quelle
2
Ja, richtig, so ist es klarer. Warum nicht Regexes verwenden, während Sie gerade dabei sind? oder ein Perl-Skript aufrufen und die Ausgabe verarbeiten?
Jean-François Fabre
Ich denke nicht, dass es eine gute Idee ist, weil os.path.join eine ziemlich gute Semantik ist ... Sie sehen es also in einem Code und verstehen sofort, was los ist.
SenhorLucas
0

Antwort auf Ihren Kommentar: "Die anderen '//' 'c:', 'c: \\' haben nicht funktioniert (C: \\ hat zwei Backslashes erstellt, C: \ hat überhaupt nicht funktioniert)"

Unter Windows os.path.join('c:', 'sourcedir') werden automatisch zwei Backslashes \\vor dem Sourcing-Verzeichnis hinzugefügt .

Um den Pfad zu beheben, wie Python auf Windows auch mit Schrägstrichen arbeitet -> ‚/‘ , fügen Sie einfach .replace('\\','/')mit os.path.joinwie folgt: -

os.path.join('c:\\', 'sourcedir').replace('\\','/')

z.B: os.path.join('c:\\', 'temp').replace('\\','/')

Ausgabe: 'C: / temp'

Pratul
quelle
0

Die vorgeschlagenen Lösungen sind interessant und bieten eine gute Referenz, sind jedoch nur teilweise zufriedenstellend. Es ist in Ordnung, das Trennzeichen manuell hinzuzufügen, wenn Sie einen bestimmten Fall haben oder das Format der Eingabezeichenfolge kennen. Es kann jedoch Fälle geben, in denen Sie dies programmgesteuert für generische Eingaben tun möchten.

Mit ein wenig Experimentieren glaube ich, dass das Kriterium darin besteht, dass der Pfadbegrenzer nicht hinzugefügt wird, wenn das erste Segment ein Laufwerksbuchstabe ist, dh ein einzelner Buchstabe, gefolgt von einem Doppelpunkt, unabhängig davon, ob er einer realen Einheit entspricht.

Beispielsweise:

import os
testval = ['c:','c:\\','d:','j:','jr:','data:']

for t in testval:
    print ('test value: ',t,', join to "folder"',os.path.join(t,'folder'))
test value:  c: , join to "folder" c:folder
test value:  c:\ , join to "folder" c:\folder
test value:  d: , join to "folder" d:folder
test value:  j: , join to "folder" j:folder
test value:  jr: , join to "folder" jr:\folder
test value:  data: , join to "folder" data:\folder

Eine bequeme Möglichkeit, die Kriterien zu testen und eine Pfadkorrektur anzuwenden, besteht darin os.path.splitdrive, das erste zurückgegebene Element mit dem Testwert zu vergleichen, z t+os.path.sep if os.path.splitdrive(t)[0]==t else t.

Prüfung:

for t in testval:
    corrected = t+os.path.sep if os.path.splitdrive(t)[0]==t else t
    print ('original: %s\tcorrected: %s'%(t,corrected),' join corrected->',os.path.join(corrected,'folder'))
original: c:    corrected: c:\  join corrected-> c:\folder
original: c:\   corrected: c:\  join corrected-> c:\folder
original: d:    corrected: d:\  join corrected-> d:\folder
original: j:    corrected: j:\  join corrected-> j:\folder
original: jr:   corrected: jr:  join corrected-> jr:\folder
original: data: corrected: data:  join corrected-> data:\folder

Es kann wahrscheinlich verbessert werden, um für nachfolgende Leerzeichen robuster zu sein, und ich habe es nur unter Windows getestet, aber ich hoffe, es gibt eine Idee. Siehe auch Os.path: Können Sie dieses Verhalten erklären? für interessante Details zu anderen Systemen als Windows.

Vincenzooo
quelle