2010-10-19 1 views
37

Предположим, у меня есть переменные dir и file, содержащие строки, представляющие каталог и имя файла, соответственно. Каков правильный способ в emacs lisp присоединиться к ним в полный путь к файлу?Каков правильный способ объединения нескольких компонентов пути в один полный путь в emacs lisp?

Например, если dir является "/usr/bin" и file является "ls", то я хочу "/usr/bin/ls". Но если вместо dir"/usr/bin/", я все равно хочу то же самое, без повторной косой черты.

+0

Формулировка заголовка («объединение нескольких компонентов пути») на самом деле немного более общая, чем фактическая проблема, но если кто-то ищет решение, которое обрабатывает значения «multiple»> 2, см. Http: // stackoverflow.com/questions/9694661 – phils

+0

Ну, если вы можете присоединиться к двум компонентам, то вы можете использовать рекурсию для объединения произвольного числа. Я полагаю, что две проблемы эквивалентны по индукции. –

ответ

47

Чтение через руководство по Directory Names, вы найдете ответ:

Учитывая имя каталога, вы можете объединить его с относительным именем файла, используя concat:

(concat dirname relfile) 

Обязательно проверьте, чтобы перед этим было указано имя файла . Если вы используете абсолютное имя файла, результаты могут быть синтаксически недействительными или ссылаться на неправильный файл.

Если вы хотите использовать файл каталога имя в создании такой комбинации, вы должны сначала преобразовать его в каталог имя с помощью file-name-as-directory:

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

Не пытайтесь конкатенации слэш по как, например, в

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

потому что это не переносится. Всегда использование file-name-as-directory.

Другие команды, которые являются полезными являются: file-name-directory, file-name-nondirectory, и другие в разделе File Name Components.

+4

Ду, неудивительно, что я вижу эти «concat» повсюду, даже наше руководство поощряет это использование. Но обычно лучше использовать 'expand-file-name', за исключением тех случаев, когда конечный результат должен быть относительным. – Stefan

+0

Почему 'expand-file-name' предпочитают более' concat'? Я не вижу практической выгоды, кроме темы стиля. –

+0

Возможно, вы также захотите назвать «convert-standard-filename» результатом, если вы заинтересованы в переносимости? –

23

Вы можете использовать expand-file-name для этого:

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

Edit: это работает только с абсолютными именами каталогов. Я думаю, что ответ Трей является предпочтительным решением.

+0

По-прежнему полезен во многих случаях. +1 –

8

Я хотел объединить несколько вложенных каталогов на путь. Первоначально я использовал несколько expand-file-name вызовов, например, так:

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

Однако это довольно многословен, и читает в обратном направлении.

Вместо этого я написал функцию, которая действует как в Python 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)))) 

Он работает следующим образом:

(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

То, что мне нужно, спасибо! –

+0

@dbr, я использую подобную функцию - см. Мой ответ ниже. Позаботьтесь о том, что я обозначил как различия и их плюсы и минусы? – sshaw

2

Если вы используете удобный файл и манипуляция каталог библиотеки f.el, вам нужно только f-join , Код ниже для тех, кто почему-то отказывается использовать эту библиотеку.

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

Это ведет себя точно так же, как os.path.join от Python.

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

string-suffix-p не существует before Emacs 24.4, поэтому я написал свой собственный в Check if a string ends with a suffix in Emacs Lisp.

+0

Это не переносимо, не так ли? Он использует косую черту в качестве разделителя каталогов и принимает корень, который начинается с косой черты. –

1

Вот что я использую:

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

Отличия от @ DBR-х:

  1. возвращает "EMACS имя каталога", то есть значение с помощью лидирующего слэша
  2. Это не расширить путь, если root относительный (см. примечания)
  3. Лечит root как корень, тогда как joindirs будет использовать первый компонент, начинающийся с "/" как корень.

Примечания

Многие функции обработки файлов (все, больше всего, ???) нормализуется лишние слеши и вызвать expand-file-name (или аналогичный) по относительным путям, так # 2 и # 3 не может на самом деле дело.