¿Por qué os.path.join () no funciona en este caso?

325

El siguiente código no se unirá, cuando se depura el comando no almacena la ruta completa sino solo la última entrada.

os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/')

Cuando pruebo esto, solo almacena la /new_sandbox/parte del código.

chrissygormley
fuente

Respuestas:

426

Las últimas cadenas no deberían comenzar con una barra inclinada. Si comienzan con una barra oblicua, se consideran un "camino absoluto" y todo lo que está delante de ellos se descarta.

Citando los documentos de Python paraos.path.join :

Si un componente es una ruta absoluta, todos los componentes anteriores se descartan y la unión continúa desde el componente de ruta absoluta.

Tenga en cuenta en Windows, el comportamiento en relación con las letras de unidad, que parece haber cambiado en comparación con las versiones anteriores de Python:

En Windows, la letra de la unidad no se restablece cuando se encuentra un componente de ruta absoluta (por ejemplo, r'\foo'). Si un componente contiene una letra de unidad, todos los componentes anteriores se descartan y la letra de unidad se restablece. Tenga en cuenta que, 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.

Craig McQueen
fuente
85
-1: ninguna cadena debe incluir un "/". Un punto de os.path.join es evitar poner barras en el camino.
S.Lott
66
El problema con str.join () es, por supuesto, que no eliminará barras dobles. Creo que este es el propósito principal para las personas que usan os.path.join. por ejemplo, '/'.join(['/etc/', '/ conf']) da como resultado tres barras: '/ etc /// conf'
Dustin Rasener el
17
@DustinRasener Puede utilizar os.path.normpathpara lograr ese objetivo.
Gareth Latty
55
No tengo idea de por qué las personas están frustradas por el comportamiento de os.path.join. En otros idiomas, la biblioteca / método de unión de ruta equivalente se comporta exactamente igual. Es más seguro y tiene más sentido.
Don Cheadle
19
Esto es frustrante porque es magia implícita , contrario a la heurística cardinal de "explícito es mejor que implícito". Y lo es . Los diseñadores de idiomas pueden creer que saben mejor, pero existen razones obvias y demostrablemente seguras para ocasionalmente querer hacer esto. Ahora no podemos. Por eso no podemos tener cosas buenas.
Cecil Curry
151

La idea os.path.join()es hacer que su programa sea multiplataforma (linux / windows / etc).

Incluso un corte lo arruina.

Por lo tanto, solo tiene sentido cuando se usa con algún tipo de punto de referencia como os.environ['HOME']o os.path.dirname(__file__).

Antony Hatchkins
fuente
75

os.path.join()se puede usar junto con os.path.seppara crear una ruta absoluta en lugar de relativa.

os.path.join(os.path.sep, 'home','build','test','sandboxes',todaystr,'new_sandbox')
ghammond
fuente
8
¡El uso de os.path.sepcomo primer elemento para construir una ruta absoluta es mejor que cualquier otra respuesta aquí! El objetivo principal de usar os.pathmétodos str en lugar de los básicos es evitar escribir /. Poner cada subdirectorio como un nuevo argumento y eliminar todas las barras también es genial. ¡Probablemente sea una buena idea asegurarse con un cheque que todaystrno comience con una barra oblicua! ;)
snooze92
3
Esto también funciona en Windows (Python 2.7.6). No interfirió con 'C: \' y se unió a los subdirectorios.
rickfoosusa 02 de
21

Para ayudar a comprender por qué este comportamiento sorprendente no es del todo terrible, considere una aplicación que acepte un nombre de archivo de configuración como argumento:

config_root = "/etc/myapp.conf/"
file_name = os.path.join(config_root, sys.argv[1])

Si la aplicación se ejecuta con:

$ myapp foo.conf

Se /etc/myapp.conf/foo.confusará el archivo de configuración .

Pero considere lo que sucede si se llama a la aplicación con:

$ myapp /some/path/bar.conf

Luego myapp debe usar el archivo de configuración en /some/path/bar.conf(y no /etc/myapp.conf/some/path/bar.confo similar).

Puede que no sea genial, pero creo que esta es la motivación para el comportamiento absoluto del camino.

David Wolever
fuente
¡Gracias! ¡Siempre había odiado este comportamiento hasta leer tu respuesta! Está documentado en docs.python.org/3.5/library/os.path.html#os.path.join , pero no es la motivación para ello.
Eli_B
En este momento, cuando necesita exactamente la solución, muchas personas lo consideran terrible.
ashrasmun
12

Es porque su '/new_sandbox/'comienza con ay, por lo /tanto, se supone que es relativo al directorio raíz. Retirar la guía /.

Ámbar
fuente
8

Para que su función sea más portátil, úsela como tal:

os.path.join(os.sep, 'home', 'build', 'test', 'sandboxes', todaystr, 'new_sandbox')

o

os.path.join(os.environ.get("HOME"), 'test', 'sandboxes', todaystr, 'new_sandbox')
NuclearPeon
fuente
8

Pruebe la combinación de split("/")y *para cadenas con combinaciones existentes.

import os

home = '/home/build/test/sandboxes/'
todaystr = '042118'
new = '/new_sandbox/'

os.path.join(*home.split("/"), todaystr, *new.split("/"))


Cómo funciona...

split("/") convierte la ruta existente en una lista: ['', 'home', 'build', 'test', 'sandboxes', '']

* delante de la lista se desglosa cada elemento de la lista con su propio parámetro

openwonk
fuente
3

Probar new_sandboxsolo con

os.path.join('/home/build/test/sandboxes/', todaystr, 'new_sandbox')

fuente
2

hazlo así, sin las barras extra

root="/home"
os.path.join(root,"build","test","sandboxes",todaystr,"new_sandbox")
ghostdog74
fuente
0

Tenga en cuenta que un problema similar puede morderlo si usa os.path.join()para incluir una extensión que ya incluye un punto, que es lo que sucede automáticamente cuando lo usa os.path.splitext(). En este ejemplo:

components = os.path.splitext(filename)
prefix = components[0]
extension = components[1]
return os.path.join("avatars", instance.username, prefix, extension)

Aunque extensionpodría ser .jpgque termines con una carpeta llamada "foobar" en lugar de un archivo llamado "foobar.jpg". Para evitar esto, debe agregar la extensión por separado:

return os.path.join("avatars", instance.username, prefix) + extension
shacker
fuente
0

que pueda stripel '/':

>>> os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/'.strip('/'))
'/home/build/test/sandboxes/04122019/new_sandbox'
suhailvs
fuente
0

Recomiendo quitar la segunda y las siguientes cadenas de la cadena os.path.sep, evitando que se interpreten como rutas absolutas:

first_path_str = '/home/build/test/sandboxes/'
original_other_path_to_append_ls = [todaystr, '/new_sandbox/']
other_path_to_append_ls = [
    i_path.strip(os.path.sep) for i_path in original_other_path_to_append_ls
]
output_path = os.path.join(first_path_str, *other_path_to_append_ls)
Igor Fobia
fuente
0
os.path.join("a", *"/b".split(os.sep))
'a/b'

Una versión más completa:

import os

def join (p, f, sep = os.sep):
    f = os.path.normpath(f)
    if p == "":
        return (f);
    else:
        p = os.path.normpath(p)
        return (os.path.join(p, *f.split(os.sep)))

def test (p, f, sep = os.sep):
    print("os.path.join({}, {}) => {}".format(p, f, os.path.join(p, f)))
    print("        join({}, {}) => {}".format(p, f, join(p, f, sep)))

if __name__ == "__main__":
    # /a/b/c for all
    test("\\a\\b", "\\c", "\\") # optionally pass in the sep you are using locally
    test("/a/b", "/c", "/")
    test("/a/b", "c")
    test("/a/b/", "c")
    test("", "/c")
    test("", "c")
Neil McGill
fuente
¿Qué pasa si os.sep es en realidad "\"? Entonces su primer ejemplo se convierte os.path.join("a", *"/b".split("\\")), lo que produce "/b"... Dudo que ese sea el resultado deseado.
NichtJens
1
Actualizado: supongo que debe dar una pista, ya que la ruta que está utilizando localmente es independiente de la del sistema operativo en el que se está ejecutando
Neil McGill,
1
Si. Alternativamente, uno podría dividirse en ambas opciones de uso común ... pero luego algún otro sistema operativo podría llegar a un tercero.
NichtJens