Подтвердить что ты не робот

Каков правильный способ объединения нескольких компонентов пути в один полный путь в emacs lisp?

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

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

4b9b3361

Ответ 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.