Каков правильный способ объединения нескольких компонентов пути в один полный путь в emacs lisp?
Предположим, что у меня есть переменные dir
и file
, содержащие строки, представляющие каталог и имя файла, соответственно. Каков правильный способ в emacs lisp присоединиться к ним в полный путь к файлу?
Например, если dir
- "/usr/bin"
и file
- "ls"
, тогда я хочу "/usr/bin/ls"
. Но если вместо dir
есть "/usr/bin/"
, я все равно хочу одно и то же, без повторной косой черты.
Ответы
Ответ 1
Прочитав руководство по Имена каталогов, вы найдете ответ:
Учитывая имя каталога, вы можете комбинировать это с относительным именем файла, используя 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
и другие в разделе Компоненты имени файла.
Ответ 2
Вы можете использовать expand-file-name
для этого:
(expand-file-name "ls" "/usr/bin")
"/usr/bin/ls"
(expand-file-name "ls" "/usr/bin/")
"/usr/bin/ls"
Изменить: это работает только с именами абсолютных каталогов. Я думаю, что ответ Трей является предпочтительным решением.
Ответ 3
Я хотел объединить несколько вложенных каталогов на путь. Первоначально я использовал несколько вызовов 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 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"
Ответ 4
Вот что я использую:
(defun catdir (root &rest dirs)
(apply 'concat (mapcar
(lambda (name) (file-name-as-directory name))
(push root dirs))))
Отличия от @dbr's:
- Возвращает "имя каталога emacs", то есть значение с завершающей косой чертой
- Он не расширяет путь, если
root
является относительным (см. примечания)
- Лечит
root
как корень, тогда как joindirs
будет использовать первый компонент, начиная с "/"
в качестве корня.
Примечания
Многие функции обработки файлов (все, большинство,???) нормализуют избыточные косые черты и вызывают expand-file-name
(или аналогичные) по относительным путям, поэтому # 2 и # 3 могут не иметь особого значения.
Ответ 5
Если вы используете удобную библиотеку манипуляций с файлами и каталогами 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))
Это ведет себя точно так же, как Python os.path.join
.
ELISP> (os-path-join "~" "a" "b" "")
"~/a/b/"
ELISP> (os-path-join "~" "a" "/b" "c")
"/b/c"
string-suffix-p
не существует перед Emacs 24.4, поэтому я написал свой собственный Проверьте, заканчивается ли строка с суффиксом в Emacs Lisp.
Ответ 6
Этот вопрос был задан в 2010 году, но на момент написания он был самым популярным среди поисковых запросов, таких как "объединить пути к файлам в elisp", поэтому я решил обновить ответ.
С 2010 года многое изменилось в мире Emacs. Там теперь выделенная библиотека для взаимодействия файлов, f.el
:
Многое вдохновлен @magnars отличным s.el и dash.el, f.el современный API для работы с файлами и каталогами в Emacs.
Эта библиотека была кратко упомянута в ответе ниже - я немного уточню это. Не пытайтесь изобретать велосипед. Вы должны использовать эту библиотеку для манипуляций с файлами. Функция, которую вы хотите, это f-join
:
(f-join "path") ;; => "path"
(f-join "path" "to") ;; => "path/to"
(f-join "/" "path" "to" "heaven") ;; => "/path/to/heaven"
Возможно, вам придется сначала установить пакет. Он должен быть доступен на MELPA.