Tại sao os.path.join () không hoạt động trong trường hợp này?


325

Đoạn mã dưới đây sẽ không tham gia, khi gỡ lỗi lệnh không lưu toàn bộ đường dẫn mà chỉ là mục cuối cùng.

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

Khi tôi kiểm tra điều này, nó chỉ lưu trữ /new_sandbox/một phần của mã.

Câu trả lời:


426

Các chuỗi sau không nên bắt đầu bằng dấu gạch chéo. Nếu họ bắt đầu bằng một dấu gạch chéo, thì họ được coi là "đường dẫn tuyệt đối" và mọi thứ trước khi chúng bị loại bỏ.

Trích dẫn tài liệu Python choos.path.join :

Nếu một thành phần là một đường dẫn tuyệt đối, tất cả các thành phần trước đó sẽ bị loại bỏ và việc nối tiếp tục từ thành phần đường dẫn tuyệt đối.

Lưu ý trên Windows, hành vi liên quan đến ký tự ổ đĩa, dường như đã thay đổi so với các phiên bản Python trước đó:

Trên Windows, ký tự ổ đĩa không được đặt lại khi gặp thành phần đường dẫn tuyệt đối (ví dụ r'\foo':). Nếu một thành phần chứa ký tự ổ đĩa, tất cả các thành phần trước đó sẽ bị loại bỏ và ký tự ổ đĩa được đặt lại. Lưu ý rằng vì có một thư mục hiện tại cho mỗi ổ đĩa, nên os.path.join("c:", "foo")đại diện cho một đường dẫn liên quan đến thư mục hiện tại trên ổ đĩa C:( c:foo), chứ không phải c:\foo.


85
-1: Không có chuỗi nào nên bao gồm "/". Một điểm chung của os.path.join là ngăn chặn bất kỳ dấu gạch chéo nào trên đường dẫn.
S.Lott

6
Tất nhiên, vấn đề với str.join () là nó sẽ không loại bỏ dấu gạch chéo kép. Tôi nghĩ rằng đây là mục đích chính cho những người sử dụng os.path.join. ví dụ: '/'.join(['/etc/', '/ conf']) dẫn đến ba dấu gạch chéo: '/ etc /// conf'
Dustin Rasener

17
@DustinRasener Bạn có thể sử dụng os.path.normpathđể đạt được mục tiêu đó.
Gareth Latty

5
không có lý do tại sao mọi người thất vọng về hành vi os.path.join. Trong các ngôn ngữ khác, thư viện / phương thức tham gia đường dẫn tương đương hoạt động giống hệt nhau. Nó an toàn hơn, và có ý nghĩa hơn.
Don Cheadle

19
Điều này thật khó chịu bởi vì nó là ma thuật ngầm , trái ngược với kinh nghiệm chính yếu của "Rõ ràng là tốt hơn là ngầm". Và nó . Các nhà thiết kế ngôn ngữ có thể tin rằng họ biết rõ hơn, nhưng vẫn tồn tại những lý do an toàn rõ ràng và cực kỳ an toàn để thỉnh thoảng muốn làm điều này. Bây giờ chúng tôi không thể. Đây là lý do tại sao chúng ta không thể có những điều tốt.
Cecil Curry

151

Ý tưởng os.path.join()là làm cho chương trình của bạn đa nền tảng (linux / windows / etc).

Thậm chí một nhát chém làm hỏng nó.

Vì vậy, nó chỉ có ý nghĩa khi được sử dụng với một số loại điểm tham chiếu như os.environ['HOME']hoặc os.path.dirname(__file__).


75

os.path.join()có thể được sử dụng cùng với os.path.sepđể tạo ra một đường dẫn tuyệt đối chứ không phải tương đối.

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

8
Việc sử dụng os.path.sepnhư là một yếu tố đầu tiên để xây dựng một đường dẫn tuyệt đối tốt hơn bất kỳ câu trả lời nào khác ở đây! Toàn bộ quan điểm của việc sử dụng os.pathchứ không phải các phương pháp str cơ bản là để tránh viết /. Đặt mọi thư mục con làm đối số mới và loại bỏ tất cả các dấu gạch chéo cũng rất tuyệt. Có lẽ sẽ là một ý tưởng tốt để đảm bảo với một kiểm tra todaystrkhông bắt đầu bằng một dấu gạch chéo! ;)
báo lại92

3
Điều này cũng hoạt động trên windows (python 2.7.6). Nó không phù hợp với 'C: \' và tham gia các thư mục con.
rickfoosusa 2/215


21

Để giúp hiểu lý do tại sao hành vi đáng ngạc nhiên này không hoàn toàn khủng khiếp, hãy xem xét một ứng dụng chấp nhận tên tệp cấu hình làm đối số:

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

Nếu ứng dụng được thực thi với:

$ myapp foo.conf

Các tập tin cấu hình /etc/myapp.conf/foo.confsẽ được sử dụng.

Nhưng hãy xem xét những gì sẽ xảy ra nếu ứng dụng được gọi với:

$ myapp /some/path/bar.conf

Sau đó myapp nên sử dụng tệp cấu hình tại /some/path/bar.conf(và không /etc/myapp.conf/some/path/bar.confhoặc tương tự).

Nó có thể không tuyệt vời, nhưng tôi tin rằng đây là động lực cho hành vi con đường tuyệt đối.


Cảm ơn! Tôi đã luôn ghét hành vi này cho đến khi đọc câu trả lời của bạn! Nó được ghi lại trong docs.python.org/3.5/l Library / os.path.html # os.path.join , nhưng không phải là động lực cho nó.
Eli_B

Thời điểm này khi bạn cần chính xác giải pháp nhiều người cho là khủng khiếp.
ashrasmun

12

Đó là bởi vì bạn '/new_sandbox/'bắt đầu với một /và do đó được coi là có liên quan đến thư mục gốc. Loại bỏ hàng đầu /.


8

Để làm cho chức năng của bạn dễ di chuyển hơn, hãy sử dụng nó như sau:

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

hoặc là

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

8

Hãy thử kết hợp split("/")*cho các chuỗi với các phép nối hiện có.

import os

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

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


Làm thế nào nó hoạt động...

split("/") biến đường dẫn hiện có thành danh sách: ['', 'home', 'build', 'test', 'sandboxes', '']

* ở phía trước danh sách chia ra từng mục của danh sách tham số riêng của nó


3

Hãy thử với new_sandboxchỉ

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

2

làm như thế này, mà không cần quá nhiều dấu gạch chéo

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

0

Lưu ý rằng một vấn đề tương tự có thể cắn bạn nếu bạn sử dụng os.path.join()để bao gồm một tiện ích mở rộng đã bao gồm một dấu chấm, đó là những gì xảy ra tự động khi bạn sử dụng os.path.splitext(). Trong ví dụ này:

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

Mặc dù extensioncó thể .jpgbạn kết thúc với một thư mục có tên "foobar" chứ không phải là một tệp có tên "foobar.jpg". Để ngăn chặn điều này, bạn cần nối thêm phần mở rộng một cách riêng biệt:

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

0

bạn có thể stripsự '/':

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

0

Tôi khuyên bạn nên tách chuỗi thứ hai và chuỗi sau chuỗi os.path.sep, ngăn không cho chúng được hiểu là đường dẫn tuyệt đối:

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)

0
os.path.join("a", *"/b".split(os.sep))
'a/b'

phiên bản đầy đủ hơn:

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")

Điều gì xảy ra nếu os.sep thực sự "\"? Sau đó, ví dụ đầu tiên của bạn trở thành os.path.join("a", *"/b".split("\\")), mang lại "/b"... Tôi nghi ngờ đó là kết quả dự định.
NichtJens

1
Đã cập nhật - Tôi cho rằng bạn phải đưa ra gợi ý vì đường dẫn bạn đang sử dụng độc lập cục bộ với hệ điều hành bạn đang chạy
Neil McGill

1
Đúng. Ngoài ra, người ta có thể tách ra trên cả hai tùy chọn thường được sử dụng ... nhưng sau đó một số HĐH khác có thể đưa ra một phần ba.
NichtJens
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.