Dlaczego os.path.join () nie działa w tym przypadku?

325

Poniższy kod nie zostanie dołączony, podczas debugowania polecenie nie przechowuje całej ścieżki, ale tylko ostatni wpis.

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

Kiedy to testuję, zapisuje tylko /new_sandbox/część kodu.

chrissygormley
źródło

Odpowiedzi:

426

Te ostatnie ciągi nie powinny zaczynać się od ukośnika. Jeśli zaczynają się od ukośnika, są uważani za „ścieżkę absolutną” i wszystko przed nimi jest odrzucane.

Cytując dokumenty Pythona dlaos.path.join :

Jeśli komponent jest ścieżką bezwzględną, wszystkie poprzednie komponenty są odrzucane, a łączenie jest kontynuowane od komponentu ścieżki bezwzględnej.

Uwaga w systemie Windows zachowanie w stosunku do liter dysku, które wydaje się ulec zmianie w porównaniu do wcześniejszych wersji Pythona:

W systemie Windows litera dysku nie jest resetowana po r'\foo'napotkaniu komponentu ścieżki bezwzględnej (np. ). Jeśli komponent zawiera literę dysku, wszystkie poprzednie komponenty są wyrzucane, a litera dysku jest resetowana. Zauważ, że ponieważ istnieje bieżący katalog dla każdego dysku, os.path.join("c:", "foo")reprezentuje ścieżkę względem bieżącego katalogu na dysku C:( c:foo), a nie c:\foo.

Craig McQueen
źródło
85
-1: Żaden ciąg nie powinien zawierać znaku „/”. Jednym z punktów os.path.join jest zapobieganie umieszczaniu ukośników na ścieżce.
S.Lott,
6
Problem z str.join () polega oczywiście na tym, że nie eliminuje podwójnych ukośników. Myślę, że jest to główny cel dla osób używających os.path.join. np. „/” .join(['/etc/ ”,„ / conf ”]) powoduje trzy ukośniki:„ / etc /// conf ”
Dustin Rasener
17
@DustinRasener Możesz użyć, os.path.normpathaby osiągnąć ten cel.
Gareth Latty
5
nie ma pojęcia, dlaczego ludzie są sfrustrowani zachowaniem os.path.join. W innych językach równoważna biblioteka / metoda łączenia ścieżek zachowuje się dokładnie tak samo. Jest bezpieczniejszy i ma większy sens.
Don Cheadle,
19
Jest to frustrujące, ponieważ jest to ukryta magia , w przeciwieństwie do kardynalnej heurystyki „jawne jest lepsze niż niejawne”. I tak jest . Projektanci języków mogą wierzyć, że wiedzą lepiej, ale istnieją oczywiste i wyraźnie bezpieczne powody, aby od czasu do czasu chcieć to zrobić. Teraz nie możemy. Dlatego nie możemy mieć dobrych rzeczy.
Cecil Curry
151

Chodzi o os.path.join()to, aby uczynić swój program wieloplatformowym (Linux / Windows / etc).

Nawet jedno cięcie go rujnuje.

Ma to więc sens tylko wtedy, gdy jest używany z jakimś punktem odniesienia, takim jak os.environ['HOME']lub os.path.dirname(__file__).

Antony Hatchkins
źródło
75

os.path.join()może być używany w połączeniu z os.path.sepdo tworzenia ścieżki bezwzględnej, a nie względnej.

os.path.join(os.path.sep, 'home','build','test','sandboxes',todaystr,'new_sandbox')
Ghammond
źródło
8
Zastosowanie os.path.sepjako pierwszego elementu do zbudowania absolutnej ścieżki jest lepsze niż jakakolwiek inna odpowiedź tutaj! Istotą używania os.pathraczej niż podstawowych metod str jest unikanie pisania /. Świetne jest także umieszczenie każdego podkatalogu jako nowego argumentu i usunięcie wszystkich ukośników. Prawdopodobnie dobrym pomysłem byłoby upewnienie się, że czek todaystrnie zaczyna się od ukośnika! ;)
drzemka92
3
Działa to również w systemie Windows (python 2.7.6). Nie wchodził w interakcje z „C: \” i dołączył do podkatalogów.
rickfoosusa
21

Aby zrozumieć, dlaczego to zaskakujące zachowanie nie jest całkowicie okropne, rozważ aplikację, która akceptuje nazwę pliku konfiguracyjnego jako argument:

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

Jeśli aplikacja jest wykonywana za pomocą:

$ myapp foo.conf

Plik konfiguracyjny /etc/myapp.conf/foo.confzostanie użyty.

Ale zastanów się, co się stanie, jeśli aplikacja zostanie wywołana z:

$ myapp /some/path/bar.conf

Następnie myapp należy użyć pliku konfiguracyjnego w /some/path/bar.conf(i nie /etc/myapp.conf/some/path/bar.conflub podobnie).

To może nie być świetne, ale uważam, że jest to motywacja do zachowania absolutnej ścieżki.

David Wolever
źródło
Dzięki! Zawsze nienawidziłem tego zachowania, dopóki nie przeczytałem twojej odpowiedzi! Jest to udokumentowane w docs.python.org/3.5/library/os.path.html#os.path.join , ale nie jest to uzasadnieniem .
Eli_B,
W tym momencie, gdy potrzebujesz dokładnie rozwiązania, wiele osób uważa za okropne.
ashrasmun
12

To dlatego, że '/new_sandbox/'zaczyna się od a, /a zatem zakłada się, że jest względny w stosunku do katalogu głównego. Usuń wiodące /.

Bursztyn
źródło
8

Aby uczynić twoją funkcję bardziej przenośną, użyj jej jako takiej:

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

lub

os.path.join(os.environ.get("HOME"), 'test', 'sandboxes', todaystr, 'new_sandbox')
NuclearPeon
źródło
8

Wypróbuj kombinację ciągów split("/")i *dla istniejących połączeń.

import os

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

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


Jak to działa...

split("/") zamienia istniejącą ścieżkę na listę: ['', 'home', 'build', 'test', 'sandboxes', '']

* przed listą wyodrębnia każdy element listy swój własny parametr

openwonk
źródło
3

Spróbuj new_sandboxtylko

os.path.join('/home/build/test/sandboxes/', todaystr, 'new_sandbox')
TY
źródło
2

zrób to w ten sposób, bez dodatkowych cięć

root="/home"
os.path.join(root,"build","test","sandboxes",todaystr,"new_sandbox")
ghostdog74
źródło
0

Pamiętaj, że podobny problem może Cię ugryźć, jeśli użyjesz os.path.join()rozszerzenia, które już zawiera kropkę, co dzieje się automatycznie, gdy używasz os.path.splitext(). W tym przykładzie:

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

Chociaż extensionmoże się zdarzyć, że .jpgotrzymasz folder o nazwie „foobar” zamiast pliku o nazwie „foobar.jpg”. Aby temu zapobiec, musisz osobno dołączyć rozszerzenie:

return os.path.join("avatars", instance.username, prefix) + extension
shacker
źródło
0

można :strip'/'

>>> os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/'.strip('/'))
'/home/build/test/sandboxes/04122019/new_sandbox'
suhailvs
źródło
0

Zalecam usunięcie drugiego i następujących ciągów os.path.sep, zapobiegając interpretacji ich jako ścieżek bezwzględnych:

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
źródło
0
os.path.join("a", *"/b".split(os.sep))
'a/b'

pełniejsza wersja:

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
źródło
Co jeśli os.sep jest w rzeczywistości "\"? Wtedy pojawia się twój pierwszy przykład os.path.join("a", *"/b".split("\\")), który daje "/b"... Wątpię, żeby to był zamierzony rezultat.
NichtJens
1
Zaktualizowano - Przypuszczam, że musisz dać wskazówkę, ponieważ ścieżka, której używasz lokalnie, jest niezależna od systemu operacyjnego, na którym
biegasz
1
Tak. Alternatywnie można podzielić na obie często używane opcje ... ale wtedy inny system operacyjny może wymyślić trzeci.
NichtJens