2010-10-19 23 views
37

Giả sử tôi có các biến số dirfile chứa các chuỗi đại diện cho thư mục và tên tệp tương ứng. Cách thích hợp trong emacs lisp để tham gia chúng vào một đường dẫn đầy đủ đến tập tin là gì?Cách chính xác để kết hợp nhiều thành phần đường dẫn vào một đường dẫn đầy đủ trong các emacs lisp là gì?

Ví dụ: nếu dir"/usr/bin"file"ls", thì tôi muốn "/usr/bin/ls". Nhưng nếu thay vào đó, dir"/usr/bin/", tôi vẫn muốn điều tương tự, không có dấu gạch chéo lặp lại.

+0

Từ ngữ của tiêu đề ("tham gia nhiều thành phần đường dẫn") thực sự chung chung hơn một chút so với vấn đề thực tế, nhưng nếu có ai tìm kiếm giải pháp xử lý giá trị của "nhiều"> 2, hãy xem http: // stackoverflow.com/questions/9694661 – phils

+0

Vâng, nếu bạn có thể tham gia hai thành phần, sau đó bạn có thể sử dụng đệ quy để tham gia một số tùy ý. Tôi cho rằng tôi xem xét hai vấn đề tương đương bằng cảm ứng. –

Trả lời

47

Đọc qua hướng dẫn cho Directory Names, bạn sẽ tìm thấy câu trả lời:

Cho một tên thư mục, bạn có thể kết hợp nó với một tên file tương đối sử dụng concat:

(concat dirname relfile) 

Hãy chắc chắn xác minh rằng tên tệp là tương đối trước khi thực hiện điều đó. Nếu bạn sử dụng tên tệp tuyệt đối, kết quả có thể không hợp lệ về cú pháp hoặc tham chiếu đến tệp không đúng.

Nếu bạn muốn sử dụng một tập tin thư mục tên trong việc đưa ra một sự kết hợp như vậy, bạn trước tiên phải chuyển đổi nó vào một thư mục tên sử dụng file-name-as-directory:

(concat (file-name-as-directory dirfile) relfile) 

Đừng cố gắng concatenating một dấu gạch chéo bởi tay, như trong

;;; Wrong! 
(concat dirfile "/" relfile) 

vì đây không phải là di động. Luôn luôn sử dụng file-name-as-directory.

lệnh

khác mà có ích là: file-name-directory, file-name-nondirectory, và những người khác trong phần File Name Components.

+4

Duh, không có thắc mắc tôi thấy những 'concat' trên tất cả các nơi, ngay cả hướng dẫn sử dụng của chúng tôi khuyến khích sử dụng này. Nhưng nói chung tốt hơn là sử dụng 'expand-file-name', ngoại trừ trong những trường hợp bạn muốn kết quả cuối cùng là một tệp tương đối. – Stefan

+0

Tại sao 'expand-file-name' được ưu tiên hơn' concat'? Khác với vấn đề về phong cách, tôi không thấy lợi ích thiết thực? –

+0

Có thể bạn cũng muốn gọi 'conversion-standard-filename' trên kết quả nếu quan tâm đến tính di động? –

23

Bạn có thể sử dụng expand-file-name cho việc này:

(expand-file-name "ls" "/usr/bin") 
"/usr/bin/ls" 
(expand-file-name "ls" "/usr/bin/") 
"/usr/bin/ls" 

Edit: này chỉ hoạt động với tên thư mục tuyệt đối. Tôi nghĩ câu trả lời của Trey là giải pháp thích hợp hơn.

+0

Vẫn hữu ích trong nhiều trường hợp. +1 –

8

Tôi muốn tham gia nhiều thư mục lồng nhau vào một đường dẫn. Ban đầu tôi sử dụng nhiều expand-file-name cuộc gọi, như vậy:

(expand-file-name "b" (expand-file-name "a" "/tmp")) 
"/tmp/a/b" 

Tuy nhiên điều này là khá dài dòng, và đọc ngược.

Thay vào đó tôi đã viết một chức năng mà hoạt động như Python của os.path.join:

(defun joindirs (root &rest dirs) 
    "Joins a series of directories together, like Python's os.path.join, 
    (dotemacs-joindirs \"/tmp\" \"a\" \"b\" \"c\") => /tmp/a/b/c" 

    (if (not dirs) 
     root 
    (apply 'joindirs 
      (expand-file-name (car dirs) root) 
      (cdr dirs)))) 

Nó hoạt động như vậy:

(joindirs "/tmp" "a" "b") 
"/tmp/a/b" 
(joindirs "~" ".emacs.d" "src") 
"/Users/dbr/.emacs.d/src" 
(joindirs "~" ".emacs.d" "~tmp") 
"/Users/dbr/.emacs.d/~tmp" 
+0

Chính xác những gì tôi cần, cảm ơn bạn! –

+0

@dbr, tôi sử dụng chức năng tương tự -xem câu trả lời của tôi ở bên dưới. Chăm sóc để bình luận về những gì tôi vạch ra là sự khác biệt và ưu/nhược điểm của họ? – sshaw

2

Nếu bạn sử dụng một tập tin và thao tác thư mục thuận tiện thư viện f.el, bạn chỉ cần f-join . Mã dưới đây dành cho những người, vì lý do nào đó từ chối sử dụng thư viện này.

(defun os-path-join (a &rest ps) 
    (let ((path a)) 
    (while ps 
     (let ((p (pop ps))) 
     (cond ((string-prefix-p "/" p) 
       (setq path p)) 
       ((or (not path) (string-suffix-p "/" p)) 
       (setq path (concat path p))) 
       (t (setq path (concat path "/" p)))))) 
    path)) 

Điều này hoạt động chính xác như Python os.path.join.

ELISP> (os-path-join "~" "a" "b" "") 
"~/a/b/" 
ELISP> (os-path-join "~" "a" "/b" "c") 
"/b/c" 

string-suffix-p không tồn tại before Emacs 24.4, vì vậy tôi đã viết riêng của tôi tại Check if a string ends with a suffix in Emacs Lisp.

+0

Đây không phải là di động, phải không? Nó sử dụng dấu gạch chéo về phía trước làm dấu tách thư mục và giả sử một gốc bắt đầu bằng dấu gạch chéo. –

1

Đây là những gì tôi sử dụng:

(defun catdir (root &rest dirs) 
    (apply 'concat (mapcar 
      (lambda (name) (file-name-as-directory name)) 
      (push root dirs)))) 

Sự khác biệt từ @ DBR:

  1. Trả về một "tên thư mục emacs", tức là một giá trị với một dấu gạch chéo
  2. Nó không mở rộng đường dẫn nếu root là tương đối (xem ghi chú)
  3. Xử lý root làm gốc, trong khi joindirs sẽ sử dụng thành phần đầu tiên bắt đầu bằng số "/" làm gốc.

Ghi chú

Nhiều chức năng xử lý tập tin (tất cả, hầu hết, ???) sẽ bình thường hóa slashes dư thừa và gọi expand-file-name (hoặc tương tự) trên đường dẫn tương đối, vì vậy # 2 và # 3 có thể không thực sự vấn đề.

Các vấn đề liên quan