Как правильно объединить несколько компонентов пути в один полный путь в emacs lisp? - PullRequest
46 голосов
/ 19 октября 2010

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

Например, если dir равно "/usr/bin" и file равно "ls", тогда я хочу "/usr/bin/ls".Но если вместо dir это "/usr/bin/", я все еще хочу то же самое, без повторного слэша.

Ответы [ 6 ]

57 голосов
/ 19 октября 2010

Прочитав руководство для Имена каталогов , вы найдете ответ:

Учитывая имя каталога, вы можете комбинировать это с относительным именем файла, используя 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 и другие в разделе Компоненты имени файла .

28 голосов
/ 19 октября 2010

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

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

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

10 голосов
/ 20 ноября 2012

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

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

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

Вместо этого я написал функцию, которая действует как Python's 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"
3 голосов
/ 27 сентября 2015

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

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

Отличия от @ dbr's:

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

Примечания

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

2 голосов
/ 04 июня 2019

Этот вопрос был задан в 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.

2 голосов
/ 14 марта 2014

Если вы используете удобную библиотеку для работы с файлами и каталогами 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 .

...