Python os.path.join en Windows

99

Estoy tratando de aprender Python y estoy creando un programa que generará un script. Quiero usar os.path.join, pero estoy bastante confundido. Según los documentos si digo:

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

yo obtengo "C:sourcedir" . Según los documentos, esto es normal, ¿verdad?

Pero cuando uso el comando copytree, Python lo generará de la forma deseada, por ejemplo:

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

Aquí está el código de error que obtengo:

WindowsError: [Error 3] El sistema no puede encontrar la ruta especificada: 'C: src /*.*'

Si envuelvo el os.path.joincon os.path.normpath, obtengo el mismo error.

Si esto os.path.joinno se puede usar de esta manera, entonces estoy confundido en cuanto a su propósito.

De acuerdo con las páginas sugeridas por Stack Overflow, las barras inclinadas no deben usarse en la combinación, eso es correcto, supongo.

Frank E.
fuente

Respuestas:

59

Windows tiene un concepto de directorio actual para cada unidad. Por eso, "c:sourcedir"significa "sourcedir" dentro del directorio C: actual, y deberá especificar un directorio absoluto.

Cualquiera de estos debería funcionar y dar el mismo resultado, pero no tengo una VM de Windows encendida en este momento para verificar:

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

fuente
8
os.path.join ('C: /', 'sourcedir') funcionó como se esperaba. Muchas gracias señor :) los otros '//' 'c:' 'c: \\' no funcionó (C: \\ creó dos barras invertidas, C: \ no funcionó en absoluto) Gracias de nuevo ghostdog74 , Smashery y Roger Pate. Estoy en deuda contigo :)
Frank E.
Lo sentimos, los saltos de línea no se guardaron en el comentario, se ve muy desordenado
Frank E.
Incluso si esto funciona en algunos casos, la respuesta de @AndreasT es una solución mucho mejor. El uso de os.sep elegirá entre / y \ según el sistema operativo.
SenhorLucas
¿Tiene algún sentido usar os.path.joino os.sepsi va a especificar de c:todos modos? c:no tiene sentido en otros sistemas operativos.
naught101
todas estas soluciones son solo parcialmente satisfactorias. Está bien agregar manualmente el separador cuando tiene un solo caso específico, pero en caso de que desee hacerlo mediante programación, ¿cuál es el criterio para el que os.path.join('c:','folder')funciona de manera diferente os.path.join('folder','file')? ¿Es por :o porque 'c: `es una unidad?
Vincenzooo
122

Para ser aún más pedante, la respuesta más consistente de Python doc sería:

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

Dado que también necesita os.sep para la ruta raíz de posix:

mypath = os.path.join(os.sep, 'usr', 'lib')
AndreasT
fuente
5
Disculpe mi ignorancia: parece que el código todavía varía entre Windows y Linux, entonces, ¿qué hace que sea os.sepsuperior?
pianoJames
3
Tenga en cuenta este problema al intentar inyectar os.sep. Solo funciona después de la letra de unidad básica. >>> os.path.join ("C: \ adiós", os.sep, "temp") 'C: \\ temp'
Jobu
1
@pianoJames mi respuesta se basa en esta para proporcionar una solución independiente del
Scott Gigante
No entiendo el sentido de todas estas soluciones "pedantes". os.sepes útil cuando desea manipular rutas sin hacer suposiciones sobre el separador. No tiene sentido usarlo os.path.join()ya que ya conoce el separador correcto. También es inútil si termina necesitando especificar explícitamente el directorio raíz por nombre (como puede ver en su propio ejemplo). ¿Por qué hacerlo en "c:" + os.seplugar de simplemente "c:\\"o en os.sep + "usr"lugar de simplemente "/usr"? También tenga en cuenta que en los shells de Win no puede, cd c:pero puede cd c:\ , lo que sugiere que el nombre raíz es en realidad c:\ .
Michael Ekoka
13

La razón por la os.path.join('C:', 'src')que no funciona como esperaba es debido a algo en la documentación a la que se vinculó:

Tenga en cuenta que en Windows, dado que hay un directorio actual para cada unidad, os.path.join ("c:", "foo") representa una ruta relativa al directorio actual en la unidad C: (c: foo), no c : \ foo.

Como dijo el perro fantasma, probablemente quieras mypath=os.path.join('c:\\', 'sourcedir')

Smashery
fuente
12

Para una solución independiente del sistema que funcione tanto en Windows como en Linux, sin importar la ruta de entrada, se podría usar os.path.join(os.sep, rootdir + os.sep, targetdir)

En ventanas:

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

En Linux:

>>> os.path.join(os.sep, "usr" + os.sep, "lib")
'/usr/lib'
Scott Gigante
fuente
1
¡Gracias! Esto es aún más útil ya que no sufre el problema que @Jobu mencionó anteriormente: os.path.join (os.sep, "C: \\ a" + os.sep, "b") devuelve "C: \\ a \\ b "en Windows.
pianoJames
1
Sin embargo, ¿cómo es que estos ejemplos son independientes del sistema? c:no existe en * nix, y usrno existe en windows ..
naught101
La llamada a la función os.path.join(os.sep, rootdir + os.sep, targetdir)es independiente del sistema precisamente porque funciona con ambos ejemplos específicos del sistema, sin necesidad de cambiar el código.
Scott Gigante
Esta solución, al igual que la publicación anterior que la inspiró, todavía se basa en configurar rootdir como rootdir = "usr" if nix else "c:". Pero el más directo y preciso rootdir = "/usr" if nix else "c:\\"funciona igual de bien, sin las os.sepacrobacias y el consiguiente rascado de cabeza. No hay peligro de que un directorio raíz en * nix comience con otra cosa que no sea una barra diagonal, o que Windows tenga directorios raíz nombrados sin dos puntos finales y una barra invertida (por ejemplo, en los shells de Win, no puede simplemente hacerlo cd c:, debería es necesario especificar la barra invertida al final), entonces, ¿por qué pretender lo contrario?
Michael Ekoka
11

Para ser pedante, probablemente no sea bueno codificar / o \ como separador de ruta. ¿Quizás esto sería lo mejor?

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

o

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

Yo diría que esto es un error de Python (Windows).

¿Por qué error?

Creo que esta declaración debería ser True

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

Pero está Falseen máquinas con Windows.

georg
fuente
1
Me inclino a estar de acuerdo en que eso constituye un error de Python. ¿Sigue siendo así? ( Escrito desde el glorioso futuro utópico de finales de 2015. )
Cecil Curry
No puedo responder esta pregunta con respecto a Windows, ya que no tengo acceso a una máquina con Windows, pero supongo que el comportamiento de Python con respecto a esta pregunta no ha cambiado. De todos modos, esta declaración tampoco es cierta para las implementaciones de Linux, ya que la primera declaración devuelve la ruta sin el separador principal (también conocido como el directorio raíz), mientras que la segunda declaración devuelve la ruta que incluye el separador principal.
georg
Así que ya no me gusta mi respuesta con respecto a esta pregunta. Pero tampoco me gusta el comportamiento de Python con respecto a esto.
georg
@Cecil Estoy en esta pregunta en este momento debido al mismo problema ... parece que todavía es el caso.
joshmcode
5

para unirse a una ruta de Windows, intente

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

Básicamente, tendrás que escapar de la barra

ghostdog74
fuente
4

Tiene algunos enfoques posibles para tratar la ruta en Windows, desde los más codificados (como usar literales de cadena sin formato o escapar de barras diagonales inversas) hasta los menos. A continuación, se muestran algunos ejemplos que funcionarán como se esperaba. Utilice lo que mejor se adapte a sus necesidades.

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 Gómez
fuente
0

Consentimiento con @ georg-

Diría entonces por qué necesitamos cojo os.path.join, mejor usar str.joino, unicode.joinpor ejemplo,

sys.path.append('{0}'.join(os.path.dirname(__file__).split(os.path.sep)[0:-1]).format(os.path.sep))
SIslam
fuente
2
sí, claro, es mucho más claro de esa manera. ¿Por qué no usar expresiones regulares mientras lo hace? o llamar a un script de perl y procesar la salida?
Jean-François Fabre
No creo que sea una buena idea porque os.path.join tiene una semántica bastante buena ... Así que lo ves en un código y entiendes de inmediato lo que está sucediendo.
SenhorLucas
0

respondiendo a su comentario: "los otros '//' 'c:', 'c: \\' no funcionó (C: \\ creó dos barras invertidas, C: \ no funcionó en absoluto)"

En Windows, el uso os.path.join('c:', 'sourcedir') agregará automáticamente dos barras diagonales inversas \\delante de sourcedir .

Para resolver la ruta, ya que Python funciona en Windows también con barras diagonales -> '/' , simplemente agregue .replace('\\','/')con os.path.joinlo siguiente: -

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

p.ej: os.path.join('c:\\', 'temp').replace('\\','/')

salida: 'C: / temp'

Pratul
fuente
0

Las soluciones propuestas son interesantes y ofrecen una buena referencia, sin embargo, solo satisfacen parcialmente. Está bien agregar manualmente el separador cuando tiene un solo caso específico o conoce el formato de la cadena de entrada, pero puede haber casos en los que desee hacerlo mediante programación en entradas genéricas.

Con un poco de experimentación, creo que el criterio es que el delimitador de ruta no se agrega si el primer segmento es una letra de unidad, es decir, una sola letra seguida de dos puntos, sin importar si corresponde a una unidad real.

Por ejemplo:

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

Una forma conveniente de probar los criterios y aplicar una corrección de ruta puede ser os.path.splitdrivecomparar el primer elemento devuelto con el valor de prueba, como t+os.path.sep if os.path.splitdrive(t)[0]==t else t.

Prueba:

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

Probablemente se pueda mejorar para que sea más robusto para los espacios finales, y lo he probado solo en Windows, pero espero que dé una idea. Consulte también Os.path: ¿puede explicar este comportamiento? para obtener detalles interesantes sobre sistemas distintos de Windows.

Vincenzooo
fuente