Python os.path.join no Windows

96

Estou tentando aprender python e estou fazendo um programa que irá gerar um script. Quero usar os.path.join, mas estou muito confuso. De acordo com a documentação, se eu disser:

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

Eu entendo "C:sourcedir". Segundo a documentação, isso é normal, certo?

Mas quando eu uso o comando copytree, o Python irá gerar a saída da maneira desejada, por exemplo:

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

Aqui está o código de erro que recebo:

WindowsError: [Erro 3] O sistema não pode encontrar o caminho especificado: 'C: src /*.*'

Se eu embrulhar os.path.joincom os.path.normpath, obtenho o mesmo erro.

Se isso os.path.joinnão pode ser usado dessa forma, fico confuso quanto ao seu propósito.

De acordo com as páginas sugeridas por Stack Overflow, barras não devem ser usadas na junção - correto, presumo?

Frank E.
fonte

Respostas:

59

O Windows tem um conceito de diretório atual para cada unidade. Por causa disso, "c:sourcedir"significa "sourcedir" dentro do diretório C: atual, e você precisará especificar um diretório absoluto.

Qualquer um deles deve funcionar e dar o mesmo resultado, mas não tenho uma VM do Windows ativada no momento para verificar novamente:

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

fonte
8
os.path.join ('C: /', 'sourcedir') funcionou como esperado. Muito obrigado, bom senhor :) os outros '//' 'c:' 'c: \\' não funcionaram (C: \\ criou duas barras invertidas, C: \ não funcionou de todo) Obrigado novamente ghostdog74 , Smashery e Roger Pate. Estou em dívida com você :)
Frank E.
Lamentamos, mas as quebras de linha não foram comentadas, parece muito confuso
Frank E.
Mesmo que isso funcione em alguns casos, a resposta de @AndreasT é uma solução muito melhor. Usar os.sep irá escolher entre / e \ dependendo do sistema operacional.
SenhorLucas
Há algum sentido em usar os.path.joinou os.sepse você vai especificar de c:qualquer maneira? c:não faz sentido em outros sistemas operacionais.
naught101
todas essas soluções são apenas parcialmente satisfatórias. Não há problema em adicionar manualmente o separador quando você tem um único caso específico, mas caso você queira fazer isso programaticamente, quais são os critérios para os quais os.path.join('c:','folder')funciona de forma diferente os.path.join('folder','file')? É por causa de :ou porque 'c: `é uma unidade?
Vincenzooo
121

Para ser ainda mais pedante, a resposta mais consistente do python doc seria:

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

Já que você também precisa de os.sep para o caminho raiz posix:

mypath = os.path.join(os.sep, 'usr', 'lib')
AndreasT
fonte
4
Desculpe minha ignorância - Parece que o código ainda varia entre o Windows e o Linux, então o que o torna os.sepsuperior?
pianoJames
3
Observe esta confusão ao tentar injetar os.sep. Ele só funciona após a letra da unidade vazia. >>> os.path.join ("C: \ adeus", os.sep, "temp") 'C: \\ temp'
Jobu
1
@pianoJames, minha resposta parte desta para fornecer uma solução independente do sistema: stackoverflow.com/a/51276165/3996580
Scott Gigante
Não entendo o sentido de todas essas soluções "pedantes". os.sepé útil quando você deseja manipular caminhos sem fazer suposições sobre o separador. É inútil usar com os.path.join()ele, pois já conhece o separador correto. Também é inútil se você acabar precisando especificar explicitamente o diretório raiz por nome (como você pode ver em seu próprio exemplo). Por que fazer em "c:" + os.sepvez de simplesmente "c:\\"ou em os.sep + "usr"vez de simplesmente "/usr"? Observe também que nos shells do Win você não cd c:pode cd c:\ , mas pode , sugerindo que o nome da raiz é realmente c:\ .
Michael Ekoka
13

O motivo de os.path.join('C:', 'src')não estar funcionando conforme o esperado é algo na documentação que você vinculou a:

Observe que no Windows, como há um diretório atual para cada unidade, os.path.join ("c:", "foo") representa um caminho relativo ao diretório atual na unidade C: (c: foo), não c : \ foo.

Como o ghostdog disse, você provavelmente quer mypath=os.path.join('c:\\', 'sourcedir')

Smashery
fonte
11

Para ser pedante, provavelmente não é bom codificar / ou \ como separador de caminho. Talvez isso seja melhor?

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

ou

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

Para uma solução independente de sistema que funciona em Windows e Linux, não importa o caminho de entrada, pode-se usar os.path.join(os.sep, rootdir + os.sep, targetdir)

No Windows:

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

No Linux:

>>> os.path.join(os.sep, "usr" + os.sep, "lib")
'/usr/lib'
Scott Gigante
fonte
1
Obrigado! Isso é ainda mais útil, pois não sofre da pegadinha que @Jobu mencionou anteriormente: os.path.join (os.sep, "C: \\ a" + os.sep, "b") retorna "C: \\ a \\ b "no Windows.
pianoJames
1
Porém, como esses exemplos são agnósticos no sistema? c:não existe no * nix e usrnão existe no Windows ..
naught101
A chamada de função os.path.join(os.sep, rootdir + os.sep, targetdir)é agnóstica do sistema precisamente porque funciona com ambos os exemplos específicos do sistema, sem a necessidade de alterar o código.
Scott Gigante de
Esta solução, assim como o post anterior que a inspirou, ainda depende da configuração de rootdir como rootdir = "usr" if nix else "c:". Mas o mais direto e preciso rootdir = "/usr" if nix else "c:\\"funciona da mesma forma, sem as os.sepacrobacias e consequentes coçar a cabeça. Não há perigo de que um diretório raiz em * nix comece com qualquer coisa diferente de uma barra, ou que o Windows tenha diretórios raiz nomeados sem dois-pontos finais e barra invertida (por exemplo, em shells do Win, você não pode simplesmente fazer cd c:, você precisa especificar a barra invertida final), então por que fingir o contrário?
Michael Ekoka
7

Eu diria que este é um bug do Python (Windows).

Por que bug?

Eu acho que esta declaração deveria ser True

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

Mas é Falseem máquinas Windows.

georg
fonte
1
Estou inclinado a concordar que isso constitui um bug do Python. Este ainda é o caso? ( Escrito a partir do glorioso futuro utópico do final de 2015. )
Cecil Curry
Não posso responder a essa pergunta com relação ao Windows, pois não tenho acesso a uma máquina com Windows, mas acho que o comportamento do python em relação a essa pergunta não mudou. De qualquer forma, esta declaração também não é verdadeira para implementações do Linux, uma vez que a primeira declaração retorna o caminho sem o separador inicial (também conhecido como o diretório raiz), enquanto a segunda declaração retorna o caminho incluindo o separador inicial.
georg
Então, na verdade, não gosto mais da minha resposta em relação a essa pergunta. Mas também não gosto do comportamento de python em relação a isso.
georg
@Cecil Estou nesta questão agora por causa do mesmo problema ... parece que ainda é o caso.
joshmcode
5

para entrar em um caminho do Windows, tente

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

basicamente, você precisará escapar da barra

ghostdog74
fonte
4

Você tem algumas abordagens possíveis para tratar o caminho no Windows, desde as mais codificadas (como o uso de literais de string brutas ou barras invertidas de escape) até as menos importantes. Aqui estão alguns exemplos que funcionarão conforme o esperado. Use o que melhor se adapta às suas necessidades.

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
fonte
0

Consentimento com @ georg-

Eu diria então por que precisamos de coxo os.path.join- melhor usar str.joinou, unicode.joinpor exemplo,

sys.path.append('{0}'.join(os.path.dirname(__file__).split(os.path.sep)[0:-1]).format(os.path.sep))
SIslam
fonte
2
sim, certo, é muuuuito mais claro dessa forma. Por que não usar regexes enquanto você está nisso? ou chamar um script perl e processar a saída?
Jean-François Fabre
Não acho que seja uma boa ideia porque os.path.join tem uma semântica muito boa ... Então você vê isso em um código e entende imediatamente o que está acontecendo.
SenhorLucas
0

respondendo ao seu comentário: "os outros '//' 'c:', 'c: \\' não funcionaram (C: \\ criou duas barras invertidas, C: \ não funcionou de todo)"

No Windows, o uso de os.path.join('c:', 'sourcedir') irá adicionar automaticamente duas barras invertidas \\na frente do código-fonte .

Para resolver o caminho, como o python funciona no Windows também com barras -> '/' , basta adicionar .replace('\\','/')com os.path.joincomo abaixo: -

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

por exemplo: os.path.join('c:\\', 'temp').replace('\\','/')

saída: 'C: / temp'

Pratul
fonte
0

As soluções propostas são interessantes e oferecem uma boa referência, porém são apenas parcialmente satisfatórias. É normal adicionar manualmente o separador quando você tem um único caso específico ou sabe o formato da string de entrada, mas pode haver casos em que você deseja fazer isso programaticamente em entradas genéricas.

Com um pouco de experimentação, acredito que o critério é que o delimitador de caminho não seja adicionado se o primeiro segmento for uma letra de unidade, ou seja, uma única letra seguida de dois pontos, não importa se corresponde a uma unidade real.

Por exemplo:

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

Uma maneira conveniente de testar os critérios e aplicar uma correção de caminho pode ser usar a os.path.splitdrivecomparação do primeiro elemento retornado com o valor de teste, como t+os.path.sep if os.path.splitdrive(t)[0]==t else t.

Teste:

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

provavelmente pode ser melhorado para ser mais robusto para espaços à direita, e eu testei apenas no Windows, mas espero que dê uma ideia. Veja também Os.path: você pode explicar esse comportamento? para detalhes interessantes sobre sistemas diferentes do Windows.

Vincenzooo
fonte