Python os.path.join su Windows


99

Sto cercando di imparare Python e sto creando un programma che produrrà uno script. Voglio usare os.path.join, ma sono piuttosto confuso. Secondo i documenti se dico:

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

Capisco "C:sourcedir". Secondo i documenti, questo è normale, giusto?

Ma quando uso il comando copytree, Python lo produrrà nel modo desiderato, ad esempio:

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

Ecco il codice di errore che ottengo:

WindowsError: [Errore 3] Il sistema non riesce a trovare il percorso specificato: "C: src /*.*"

Se avvolgo il os.path.joincon os.path.normpathottengo lo stesso errore.

Se questo os.path.joinnon può essere usato in questo modo, allora sono confuso sul suo scopo.

Secondo le pagine suggerite da Stack Overflow, le barre non dovrebbero essere usate nel join: è corretto, presumo?

Risposte:


59

Windows ha un concetto di directory corrente per ogni unità. Per questo "c:sourcedir"motivo , significa "sourcedir" all'interno della directory C: corrente e dovrai specificare una directory assoluta.

Ognuno di questi dovrebbe funzionare e dare lo stesso risultato, ma al momento non ho una VM Windows attivata per ricontrollare:

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

8
os.path.join ('C: /', 'sourcedir') ha funzionato come previsto. La ringrazio molto bene signore :) gli altri "//" c: "c: \\" non hanno funzionato (C: \\ ha creato due backslash, C: \ non ha funzionato affatto) Grazie ancora ghostdog74 , Smashery e Roger Pate. Sono in debito con te :)
Frank E.

Spiacenti, le interruzioni di riga non sono state mantenute nei commenti, sembra molto disordinato
Frank E.

Anche se in alcuni casi funziona, la risposta di @AndreasT è una soluzione molto migliore. Utilizzando os.sep si sceglierà tra / e \ a seconda del sistema operativo.
Senhor Lucas

Ha senso usare os.path.joino os.sepse hai intenzione di specificare c:comunque? c:non ha senso su altri sistemi operativi.
nought101

tutte queste soluzioni sono solo parzialmente soddisfacenti. Va bene aggiungere manualmente il separatore quando hai un singolo caso specifico, ma nel caso in cui lo desideri a livello di programmazione, qual è il criterio per il quale os.path.join('c:','folder')funziona in modo diverso os.path.join('folder','file')? È a causa di :o perché 'c: `è un'unità?
Vincenzooo l'

122

Per essere ancora più pedanti, la risposta più coerente con il documento Python sarebbe:

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

Dal momento che hai anche bisogno di os.sep per il percorso di root posix:

mypath = os.path.join(os.sep, 'usr', 'lib')

5
Scusa la mia ignoranza - Sembra che il codice vari ancora tra Windows e Linux, quindi cosa lo rende os.sepsuperiore?
pianoJames

3
Si prega di notare questo problema quando si tenta di iniettare os.sep. Funziona solo dopo la lettera di unità nuda. >>> os.path.join ("C: \ goodbye", os.sep, "temp") 'C: \\ temp'
Jobu

1
@pianoJames la mia risposta si basa su questa per fornire una soluzione indipendente dal sistema: stackoverflow.com/a/51276165/3996580
Scott Gigante

Non capisco il senso di tutte queste soluzioni "pedanti". os.sepè utile quando si desidera manipolare i percorsi senza fare supposizioni sul separatore. È inutile usarlo os.path.join()poiché conosce già il separatore giusto. È anche inutile se finisci per aver bisogno di specificare esplicitamente la directory principale per nome (come puoi vedere nel tuo esempio). Perché "c:" + os.sepinvece di semplicemente "c:\\", o os.sep + "usr"invece di semplicemente "/usr"? Nota anche che nelle shell Win non puoi cd c:ma puoi cd c:\ , suggerendo che il nome della radice è effettivamente c:\ .
Michael Ekoka

13

Il motivo os.path.join('C:', 'src')non funziona come ti aspetti è a causa di qualcosa nella documentazione a cui ti sei collegato:

Nota che su Windows, poiché esiste una directory corrente per ogni unità, os.path.join ("c:", "foo") rappresenta un percorso relativo alla directory corrente sull'unità C: (c: foo), non c : \ foo.

Come ha detto il cane fantasma, probabilmente lo vuoi mypath=os.path.join('c:\\', 'sourcedir')


12

Per una soluzione indipendente dal sistema che funziona sia su Windows che su Linux, indipendentemente dal percorso di input, è possibile utilizzare os.path.join(os.sep, rootdir + os.sep, targetdir)

Su Windows:

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

Su Linux:

>>> os.path.join(os.sep, "usr" + os.sep, "lib")
'/usr/lib'

1
Grazie! Ciò è ancora più utile poiché non soffre del gotcha menzionato in precedenza da @Jobu: os.path.join (os.sep, "C: \\ a" + os.sep, "b") restituisce "C: \\ a \\ b "su Windows.
pianoJames

1
In che modo entrambi questi esempi sono indipendenti dal sistema? c:non esiste su * nix e usrnon esiste su Windows ..
nought101

La chiamata di funzione os.path.join(os.sep, rootdir + os.sep, targetdir)è indipendente dal sistema proprio perché funziona con entrambi gli esempi specifici del sistema, senza la necessità di modificare il codice.
Scott Gigante

Questa soluzione, proprio come il post precedente che l'ha ispirata, si basa ancora sull'impostazione di rootdir come rootdir = "usr" if nix else "c:". Ma il più diretto e preciso rootdir = "/usr" if nix else "c:\\"funziona altrettanto bene, senza le os.sepacrobazie e il conseguente grattarsi la testa. Non c'è pericolo che una directory di root su * nix inizi con qualcosa di diverso da una barra in avanti, o che Windows abbia le directory di root denominate senza i due punti finali e la barra rovesciata (ad esempio nelle shell di Win, non puoi semplicemente fare cd c:, dovresti è necessario specificare la barra rovesciata finale), quindi perché fingere il contrario?
Michael Ekoka

11

Per essere pedanti, probabilmente non è bene codificare o / o \ come separatore di percorso. Forse sarebbe meglio?

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

o

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

7

Direi che questo è un bug di Python (Windows).

Perché bug?

Penso che questa affermazione dovrebbe essere True

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

Ma è Falsesu macchine Windows.


1
Sono propenso a concordare sul fatto che ciò costituisce un bug di Python. È ancora così? ( Scritto dal glorioso futuro utopico della fine del 2015 )
Cecil Curry

Non posso rispondere a questa domanda riguardo a Windows, poiché non ho accesso a una macchina Windows, ma immagino che il comportamento di Python riguardo a questa domanda non sia cambiato. Ad ogni modo, questa affermazione non è vera anche per le implementazioni Linux, poiché la prima istruzione restituisce il percorso senza il separatore iniziale (ovvero la directory principale), mentre la seconda istruzione restituisce il percorso che include il separatore iniziale.
georg

Quindi in realtà non mi piace più la mia risposta riguardo a questa domanda. Ma non mi piace nemmeno il comportamento di Python riguardo a questo.
georg

@ Cecil Sono su questa domanda in questo momento a causa dello stesso problema ... sembra che sia ancora così.
joshmcode

5

per unirti a un percorso Windows, prova

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

fondamentalmente, dovrai sfuggire alla barra


4

Hai alcuni possibili approcci per trattare il percorso su Windows, da quelli più hardcoded (come l'utilizzo di stringhe letterali non elaborate o l'escape dei backslash) a quelli più piccoli. Di seguito sono riportati alcuni esempi che funzioneranno come previsto. Usa ciò che si adatta meglio alle tue esigenze.

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

0

Consenso con @ georg-

Direi quindi perché abbiamo bisogno di zoppo os.path.join- meglio usare str.joino unicode.joinad es

sys.path.append('{0}'.join(os.path.dirname(__file__).split(os.path.sep)[0:-1]).format(os.path.sep))

2
sì, giusto, è molto più chiaro in questo modo. Perché non usare le espressioni regolari mentre ci sei? o chiamare uno script perl ed elaborare l'output?
Jean-François Fabre

Non penso sia una buona idea perché os.path.join è una semantica piuttosto buona ... Quindi lo vedi in un codice e capisci subito cosa sta succedendo.
SenhorLucas

0

rispondendo al tuo commento: "gli altri '//' 'c:', 'c: \\' non funzionavano (C: \\ creava due barre rovesciate, C: \ non funzionava affatto)"

Su Windows, l'utilizzo os.path.join('c:', 'sourcedir') aggiungerà automaticamente due barre rovesciate \\davanti a sourcedir .

Per risolvere il percorso, poiché Python funziona su Windows anche con barre in avanti -> '/' , aggiungi semplicemente .replace('\\','/')con os.path.joincome sotto: -

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

per esempio: os.path.join('c:\\', 'temp').replace('\\','/')

uscita: 'C: / temp'


0

Le soluzioni proposte sono interessanti e offrono una buona referenza, tuttavia sono solo parzialmente soddisfacenti. Va bene aggiungere manualmente il separatore quando si ha un singolo caso specifico o si conosce il formato della stringa di input, ma ci possono essere casi in cui si desidera farlo a livello di codice su input generici.

Con un po 'di sperimentazione, credo che il criterio sia che il delimitatore di percorso non viene aggiunto se il primo segmento è una lettera di unità, ovvero una singola lettera seguita da due punti, indipendentemente dal fatto che corrisponda a un'unità reale.

Per esempio:

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

Un modo conveniente per verificare i criteri e applicare una correzione del percorso può essere quello di utilizzare il os.path.splitdriveconfronto del primo elemento restituito con il valore del test, come 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

probabilmente può essere migliorato per essere più robusto per gli spazi finali, e l'ho testato solo su Windows, ma spero che dia un'idea. Vedi anche Os.path: puoi spiegare questo comportamento? per dettagli interessanti su sistemi diversi da Windows.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.