Python os.path.join sous Windows

98

J'essaie d'apprendre le python et je crée un programme qui produira un script. Je veux utiliser os.path.join, mais je suis assez confus. D'après la documentation si je dis:

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

Je reçois "C:sourcedir". Selon la documentation, c'est normal, non?

Mais lorsque j'utilise la commande copytree, Python le sortira de la manière souhaitée, par exemple:

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

Voici le code d'erreur que j'obtiens:

WindowsError: [Erreur 3] Le système ne trouve pas le chemin spécifié: 'C: src /*.*'

Si j'emballe le os.path.joinavec, os.path.normpathj'obtiens la même erreur.

Si cela os.path.joinne peut pas être utilisé de cette façon, alors je ne sais pas quel but.

Selon les pages suggérées par Stack Overflow, les barres obliques ne doivent pas être utilisées dans la jointure - c'est correct, je suppose?

Frank E.
la source

Réponses:

59

Windows a un concept de répertoire actuel pour chaque lecteur. Pour cette raison, "c:sourcedir"signifie "sourcedir" dans le répertoire C: actuel, et vous devrez spécifier un répertoire absolu.

N'importe lequel de ceux-ci devrait fonctionner et donner le même résultat, mais je n'ai pas de machine virtuelle Windows activée pour le moment pour vérifier:

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

la source
8
os.path.join ('C: /', 'sourcedir') a fonctionné comme prévu. Je vous remercie beaucoup, bon monsieur :) les autres '//' 'c:' 'c: \\' n'ont pas fonctionné (C: \\ a créé deux contre-obliques, C: \ n'a pas fonctionné du tout) Merci encore ghostdog74 , Smashery et Roger Pate. Je vous suis redevable :)
Frank E.
Désolé, les sauts de ligne n'ont pas été conservés dans les commentaires, cela semble très compliqué
Frank E.
Même si cela fonctionne dans certains cas, la réponse de @AndreasT est une bien meilleure solution. Utiliser os.sep choisira entre / et \ en fonction du système d'exploitation.
SenhorLucas
Y a-t-il un intérêt à utiliser os.path.joinou os.sepsi vous allez le spécifier c:quand même? c:n'a aucun sens sur les autres systèmes d'exploitation.
naught101
toutes ces solutions ne sont que partiellement satisfaisantes. Il est correct d'ajouter manuellement le séparateur lorsque vous avez un seul cas spécifique, mais si vous souhaitez le faire par programme, quels sont les critères pour lesquels os.path.join('c:','folder')fonctionne différemment de os.path.join('folder','file')? Est-ce à cause du :ou parce que «c:» est un lecteur?
Vincenzooo
121

Pour être encore plus pédant, la réponse la plus cohérente de la documentation python serait:

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

Puisque vous avez également besoin de os.sep pour le chemin racine de posix:

mypath = os.path.join(os.sep, 'usr', 'lib')
AndreasT
la source
4
Excusez mon ignorance - Il semble que le code varie toujours entre Windows et Linux, alors qu'est-ce qui le rend os.sepsupérieur?
pianoJames
3
Veuillez noter ce snafu lorsque vous essayez d'injecter os.sep. Cela ne fonctionne qu'après la lettre de lecteur nue. >>> os.path.join ("C: \ goodbye", os.sep, "temp") 'C: \\ temp'
Jobu
1
@pianoJames ma réponse s'appuie sur celle-ci pour fournir une solution indépendante du système: stackoverflow.com/a/51276165/3996580
Scott Gigante
Je ne comprends pas l'intérêt de toutes ces solutions «pédantes». os.sepest utile lorsque vous souhaitez manipuler des chemins sans faire d'hypothèses sur le séparateur. Il est inutile de l'utiliser os.path.join()car il connaît déjà le bon séparateur. Il est également inutile si vous finissez par avoir besoin de spécifier explicitement le répertoire racine par son nom (comme vous pouvez le voir dans votre propre exemple). Pourquoi faire "c:" + os.sepau lieu de simplement "c:\\", ou os.sep + "usr"au lieu de simplement "/usr"? Notez également que dans les shells Win, vous ne pouvez pas cd c:mais vous pouvez cd c:\ , ce qui suggère que le nom racine est en fait c:\ .
Michael Ekoka
13

La raison os.path.join('C:', 'src')ne fonctionne pas comme prévu est à cause de quelque chose dans la documentation que vous avez lié:

Notez que sous Windows, comme il existe un répertoire courant pour chaque lecteur, os.path.join ("c:", "foo") représente un chemin relatif au répertoire courant sur le lecteur C: (c: foo), pas c : \ foo.

Comme Ghostdog l'a dit, tu veux probablement mypath=os.path.join('c:\\', 'sourcedir')

Smashery
la source
12

Pour une solution indépendante du système qui fonctionne à la fois sur Windows et Linux, quel que soit le chemin d'entrée, on pourrait utiliser os.path.join(os.sep, rootdir + os.sep, targetdir)

Sur Windows:

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

Sous Linux:

>>> os.path.join(os.sep, "usr" + os.sep, "lib")
'/usr/lib'
Scott Gigante
la source
1
Merci! Ceci est d'autant plus utile qu'il ne souffre pas du gotcha mentionné précédemment par @Jobu: os.path.join (os.sep, "C: \\ a" + os.sep, "b") renvoie "C: \\ a \\ b "sous Windows.
pianoJames
1
Mais en quoi l'un de ces exemples est-il indépendant du système? c:n'existe pas sur * nix, et usrn'existe pas sur Windows ..
naught101
L'appel de fonction os.path.join(os.sep, rootdir + os.sep, targetdir)est indépendant du système précisément parce qu'il fonctionne avec ces deux exemples spécifiques au système, sans avoir besoin de changer le code.
Scott Gigante
Cette solution, tout comme le post précédent qui l'a inspirée, repose toujours sur la définition de rootdir comme rootdir = "usr" if nix else "c:". Mais le plus direct et précis rootdir = "/usr" if nix else "c:\\"fonctionne aussi bien, sans les os.sepacrobaties et les grattages de tête qui en résultent. Il n'y a aucun danger qu'un répertoire racine sur * nix commence par autre chose qu'une barre oblique, ou que Windows ait des répertoires racine nommés sans deux-points et une barre oblique inverse (par exemple, dans les shells Win, vous ne pouvez pas simplement faire cd c:, vous besoin de spécifier la barre oblique inverse de fin), alors pourquoi prétendre le contraire?
Michael Ekoka
11

Pour être pédant, il n'est probablement pas bon de coder en dur soit / ou \ comme séparateur de chemin. Ce serait peut-être mieux?

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

ou

mypath = os.path.join('c:' + os.sep, 'sourcedir')
Matt Ball
la source
7

Je dirais que c'est un bogue Python (Windows).

Pourquoi bug?

Je pense que cette déclaration devrait être True

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

Mais c'est Falsesur les machines Windows.

Georg
la source
1
Je suis enclin à convenir que cela constitue un bogue Python. Est-ce toujours le cas? ( Écrit à partir du futur utopique glorieux de la fin de 2015. )
Cecil Curry
Je ne peux pas répondre à cette question en ce qui concerne Windows, car je n'ai pas accès à une machine Windows, mais je suppose que le comportement de Python concernant cette question n'a pas changé. Quoi qu'il en soit, cette instruction n'est pas non plus vraie pour les implémentations Linux, car la première instruction renvoie le chemin sans le séparateur principal (alias le répertoire racine), tandis que la deuxième instruction renvoie le chemin comprenant le séparateur principal.
georg
Donc je n'aime plus ma réponse à cette question. Mais je n'aime pas non plus le comportement de Python à ce sujet.
georg
@Cecil Je suis sur cette question en ce moment à cause du même problème ... cela semble toujours être le cas.
joshmcode
5

pour rejoindre un chemin Windows, essayez

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

en gros, vous devrez échapper à la barre oblique

ghostdog74
la source
4

Vous avez quelques approches possibles pour traiter le chemin sur Windows, des plus codées en dur (comme l'utilisation de littéraux de chaîne bruts ou l'échappement de barres obliques inverses) aux plus petites. Voici quelques exemples qui fonctionneront comme prévu. Utilisez ce qui correspond le mieux à vos besoins.

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
la source
0

Consentement avec @ georg-

Je dirais alors pourquoi nous avons besoin de boiteux os.path.join- mieux utiliser str.joinou unicode.joinpar exemple

sys.path.append('{0}'.join(os.path.dirname(__file__).split(os.path.sep)[0:-1]).format(os.path.sep))
SIslam
la source
2
oui, c'est waaaaayy plus clair comme ça. Pourquoi ne pas utiliser les regex pendant que vous y êtes? ou appeler un script perl et traiter la sortie?
Jean-François Fabre
Je ne pense pas que ce soit une bonne idée car os.path.join est une assez bonne sémantique ... Donc vous le voyez dans un code et vous comprenez tout de suite ce qui se passe.
SenhorLucas
0

répondant à votre commentaire: "les autres '//' 'c:', 'c: \\' n'ont pas fonctionné (C: \\ a créé deux contre-obliques, C: \ n'a pas fonctionné du tout)"

Sur Windows, l'utilisation os.path.join('c:', 'sourcedir') ajoutera automatiquement deux barres obliques inverses \\devant sourcedir .

Pour résoudre le chemin, comme python fonctionne également sur Windows avec des barres obliques -> '/' , ajoutez simplement .replace('\\','/')avec os.path.joincomme ci-dessous: -

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

par exemple: os.path.join('c:\\', 'temp').replace('\\','/')

sortie: 'C: / temp'

Pratul
la source
0

Les solutions proposées sont intéressantes et offrent une bonne référence, mais elles ne sont que partiellement satisfaisantes. Il est possible d'ajouter manuellement le séparateur lorsque vous avez un seul cas spécifique ou que vous connaissez le format de la chaîne d'entrée, mais il peut y avoir des cas où vous souhaitez le faire par programme sur des entrées génériques.

Avec un peu d'expérimentation, je pense que le critère est que le délimiteur de chemin n'est pas ajouté si le premier segment est une lettre de lecteur, c'est-à-dire une seule lettre suivie de deux points, peu importe si cela correspond à une unité réelle.

Par exemple:

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 moyen pratique de tester les critères et d'appliquer une correction de chemin peut être d'utiliser la os.path.splitdrivecomparaison du premier élément renvoyé à la valeur de test, comme t+os.path.sep if os.path.splitdrive(t)[0]==t else t.

Tester:

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

il peut probablement être amélioré pour être plus robuste pour les espaces de fin, et je ne l'ai testé que sur windows, mais j'espère que cela donne une idée. Voir aussi Os.path: pouvez-vous expliquer ce comportement? pour des détails intéressants sur les systèmes autres que Windows.

Vincenzooo
la source