Python os.path.join w systemie Windows

99

Próbuję nauczyć się języka Python i tworzę program, który wyświetli skrypt. Chcę użyć os.path.join, ale jestem dość zdezorientowany. Według dokumentów, jeśli powiem:

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

Rozumiem "C:sourcedir". Według doktorów to normalne, prawda?

Ale kiedy używam polecenia copytree, Python wyświetli je w pożądany sposób, na przykład:

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

Oto kod błędu, który otrzymuję:

WindowsError: [Błąd 3] System nie może znaleźć określonej ścieżki: „C: src /*.*”

Gdybym owinąć os.path.joinsię os.path.normpathuzyskać ten sam błąd.

Jeśli os.path.joinnie można tego użyć w ten sposób, jestem zdezorientowany co do jego celu.

Zgodnie ze stronami sugerowanymi przez Stack Overflow, ukośniki nie powinny być używane w łączeniu - to prawda, zakładam?

Frank E.
źródło

Odpowiedzi:

60

Windows ma koncepcję bieżącego katalogu dla każdego dysku. Z tego powodu "c:sourcedir"oznacza „źródło źródła” w bieżącym katalogu C :, a będziesz musiał określić katalog bezwzględny.

Każde z nich powinno działać i dać ten sam wynik, ale nie mam w tej chwili uruchomionej maszyny wirtualnej z systemem Windows, aby dwukrotnie sprawdzić:

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

źródło
8
os.path.join ('C: /', 'sourcedir') działało zgodnie z oczekiwaniami. Dziękuję bardzo dobry panie :) inne '//' 'c:' 'c: \\' nie zadziałały (C: \\ utworzył dwa ukośniki odwrotne, C: \ w ogóle nie działał) Jeszcze raz dziękuję ghostdog74 , Smashery i Roger Pate. Jestem twoim długiem :)
Frank E.
Przepraszamy, podziały wierszy nie zostały zachowane w komentarzach, wygląda to bardzo niechlujnie
Frank E.
Nawet jeśli to zadziała w niektórych przypadkach, odpowiedź @AndreasT jest znacznie lepszym rozwiązaniem. Użycie os.sep spowoduje wybór pomiędzy / i \ w zależności od systemu operacyjnego.
SenhorLucas,
Czy jest jakiś sens używania os.path.joinlub os.sepczy c:mimo to zamierzasz określić ? c:nie ma sensu w innych systemach operacyjnych.
naught101
wszystkie te rozwiązania są tylko częściowo satysfakcjonujące. Jeśli masz jeden konkretny przypadek, możesz ręcznie dodać separator, ale jeśli chcesz to zrobić programowo, jakie kryteria os.path.join('c:','folder')działają inaczej niż os.path.join('folder','file')? Czy to dlatego, :że „c:” jest dyskiem?
Vincenzooo
125

Aby być jeszcze bardziej pedantycznym, najbardziej spójną odpowiedzią w Pythonie byłoby:

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

Ponieważ potrzebujesz również os.sep jako ścieżki głównej posix:

mypath = os.path.join(os.sep, 'usr', 'lib')
AndreasT
źródło
5
Przepraszam za moją ignorancję - wygląda na to, że kod nadal różni się między systemami Windows i Linux, więc co sprawia, że ​​jest os.seplepszy?
pianoJames
3
Proszę zwrócić uwagę na to snafu podczas próby wstrzyknięcia os.sep. Działa tylko po literze samego dysku. >>> os.path.join ("C: \ goodbye", os.sep, "temp") 'C: \\ temp'
Jobu
1
@pianoJames moja odpowiedź opiera się na tym, aby zapewnić rozwiązanie niezależne od systemu: stackoverflow.com/a/51276165/3996580
Scott Gigante.
Nie rozumiem sensu tych wszystkich „pedantycznych” rozwiązań. os.sepjest przydatne, gdy chcesz manipulować ścieżkami bez robienia z góry założeń dotyczących separatora. Nie ma sensu go używać, os.path.join()ponieważ zna już właściwy separator. Nie ma też sensu, jeśli w końcu będziesz musiał jawnie określić katalog główny według nazwy (jak widać na własnym przykładzie). Dlaczego "c:" + os.sepzamiast po prostu "c:\\", czy os.sep + "usr"zamiast po prostu "/usr"? Zauważ również, że w powłokach Win nie możesz, cd c:ale możesz cd c:\ , co sugeruje, że nazwa roota to faktycznie c:\ .
Michael Ekoka
13

Przyczyną os.path.join('C:', 'src')niedziałania zgodnie z oczekiwaniami jest coś w dokumentacji, do której utworzyłeś link:

Zauważ, że w systemie Windows, ponieważ dla każdego dysku istnieje katalog bieżący, os.path.join ("c:", "foo") reprezentuje ścieżkę względem bieżącego katalogu na dysku C: (c: foo), a nie c :\bla.

Jak powiedział Ghostdog, prawdopodobnie chcesz mypath=os.path.join('c:\\', 'sourcedir')

Kowalstwo
źródło
12

W przypadku rozwiązania niezależnego od systemu, które działa zarówno w systemie Windows, jak i Linux, niezależnie od ścieżki wejściowej, można by użyć os.path.join(os.sep, rootdir + os.sep, targetdir)

W systemie Windows:

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

W systemie Linux:

>>> os.path.join(os.sep, "usr" + os.sep, "lib")
'/usr/lib'
Scott Gigante
źródło
1
Dzięki! Jest to nawet bardziej przydatne, ponieważ nie cierpi z powodu problemu, o którym wspomniałem wcześniej @Jobu: os.path.join (os.sep, „C: \\ a” + os.sep, „b”) zwraca „C: \\ a \\ b ”w systemie Windows.
pianoJames
1
Jak jednak którykolwiek z tych przykładów jest agnostyczny? c:nie istnieje na * nix i usrnie istnieje w
systemie
Wywołanie funkcji os.path.join(os.sep, rootdir + os.sep, targetdir)jest niezależne od systemu właśnie dlatego, że działa z obydwoma przykładami specyficznymi dla systemu, bez konieczności zmiany kodu.
Scott Gigante,
To rozwiązanie, podobnie jak wcześniejszy post, który go zainspirował, nadal opiera się na ustawieniu rootdir rootdir = "usr" if nix else "c:". Ale bardziej bezpośredni i dokładny rootdir = "/usr" if nix else "c:\\"działa równie dobrze, bez os.sepakrobacji i wynikającego z tego drapania się po głowie. Nie ma niebezpieczeństwa, że ​​katalog główny w * nix zacznie się od czegoś innego niż ukośnik lub że Windows będzie miał katalogi główne nazwane bez końcowego dwukropka i odwrotnego ukośnika (np. W powłokach Win nie można po prostu zrobić cd c:, trzeba określić końcowy ukośnik odwrotny), więc po co udawać, że jest inaczej?
Michael Ekoka
11

Aby być pedantycznym, prawdopodobnie nie jest dobrze zakodować na stałe / lub \ jako separator ścieżki. Może to byłoby najlepsze?

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

lub

mypath = os.path.join('c:' + os.sep, 'sourcedir')
Matt Ball
źródło
7

Powiedziałbym, że to błąd Pythona (Windows).

Dlaczego błąd?

Myślę, że takie stwierdzenie powinno być True

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

Ale jest Falsena komputerach z systemem Windows.

Georg
źródło
1
Jestem skłonny się zgodzić, że stanowi to błąd Pythona. Czy nadal tak jest? ( Napisane na podstawie chwalebnej utopijnej przyszłości końca 2015 r. )
Cecil Curry,
Nie mogę odpowiedzieć na to pytanie w odniesieniu do okien, ponieważ nie mam dostępu do komputera z systemem Windows, ale wydaje mi się, że zachowanie Pythona w odniesieniu do tego pytania nie uległo zmianie. W każdym razie ta instrukcja również nie jest prawdą w przypadku implementacji Linuksa, ponieważ pierwsza instrukcja zwraca ścieżkę bez wiodącego separatora (czyli katalogu głównego), podczas gdy druga instrukcja zwraca ścieżkę zawierającą wiodący separator.
georg
Więc właściwie nie podoba mi się już moja odpowiedź na to pytanie. Ale też nie podoba mi się zachowanie Pythona w tym zakresie.
georg
@Cecil Mam teraz to pytanie z powodu tego samego problemu ... wydaje się, że nadal tak jest.
joshmcode
5

aby dołączyć do ścieżki systemu Windows, spróbuj

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

w zasadzie będziesz musiał uciec przed ukośnikiem

ghostdog74
źródło
4

Masz kilka możliwych podejść do traktowania ścieżki w systemie Windows, od najbardziej zakodowanych na stałe (takich jak użycie surowych literałów ciągu lub uciekających ukośników odwrotnych) do najmniejszych. Oto kilka przykładów, które będą działać zgodnie z oczekiwaniami. Użyj tego, co lepiej odpowiada Twoim potrzebom.

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
źródło
0

Zgoda z @ georg-

Powiedziałbym wtedy po co kulawej os.path.join- lepiej użyć str.joinlub unicode.joinnp

sys.path.append('{0}'.join(os.path.dirname(__file__).split(os.path.sep)[0:-1]).format(os.path.sep))
SIslam
źródło
2
tak, racja, w ten sposób jest oaaaayy jaśniejszy. Dlaczego nie używać wyrażeń regularnych, gdy już to robisz? lub wywołać skrypt Perla i przetworzyć wynik?
Jean-François Fabre
Myślę, że to nie jest dobry pomysł, ponieważ os.path.join to całkiem dobra semantyka ... Więc widzisz to w kodzie i od razu rozumiesz, co się dzieje.
SenhorLucas,
0

odpowiadając na Twój komentarz: „pozostali '//' 'c:', 'c: \\' nie zadziałały (C: \\ utworzył dwa ukośniki odwrotne, C: \ w ogóle nie działał)”

W oknach użycie os.path.join('c:', 'sourcedir') spowoduje automatyczne dodanie dwóch odwrotnych ukośników \\przed sourcedir .

Aby rozwiązać ścieżkę, jak pyton działa na Windows również z ukośniki forward -> „/” , wystarczy dodać .replace('\\','/')ze os.path.joinjak poniżej: -

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

na przykład: os.path.join('c:\\', 'temp').replace('\\','/')

wyjście: 'C: / temp'

Pratul
źródło
0

Proponowane rozwiązania są ciekawe i stanowią dobrą referencję, ale tylko częściowo satysfakcjonują. Ręczne dodawanie separatora jest ok, gdy masz jedną konkretną przypadek lub znasz format ciągu wejściowego, ale mogą wystąpić przypadki, w których chcesz to zrobić programowo na ogólnych danych wejściowych.

Po odrobinie eksperymentowania uważam, że kryterium jest takie, że ogranicznik ścieżki nie jest dodawany, jeśli pierwszy segment jest literą dysku, co oznacza pojedynczą literę, po której następuje dwukropek, bez względu na to, czy odpowiada rzeczywistej jednostce.

Na przykład:

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

Wygodnym sposobem sprawdzenia kryteriów i zastosowania korekty ścieżki może być os.path.splitdriveporównanie pierwszego zwróconego elementu z wartością testową, na przykład t+os.path.sep if os.path.splitdrive(t)[0]==t else t.

Test:

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

prawdopodobnie można go ulepszyć, aby był bardziej odporny na spacje końcowe, a testowałem go tylko na oknach, ale mam nadzieję, że daje pomysł. Zobacz także Os. Ścieżka: czy możesz wyjaśnić to zachowanie? dla interesujących szczegółów systemów innych niż okna.

Vincenzooo
źródło