63

假设我有变量dirfile包含分别代表目录和文件名的字符串。emacs lisp中将它们加入文件完整路径的正确方法是什么?

例如,如果diris"/usr/bin"fileis "ls",那么我想要"/usr/bin/ls"。但如果改为diris "/usr/bin/",我仍然想要同样的东西,没有重复的斜线。

4

8 回答 8

78

通读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和“文件名组件”部分中的其他命令。

于 2010-10-19T01:50:46.110 回答
32

您可以expand-file-name为此使用:

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

编辑:这仅适用于绝对目录名称。我认为 Trey 的回答是更可取的解决方案。

于 2010-10-19T01:22:15.200 回答
12

我想将多个嵌套目录加入到路径中。最初我使用了多个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"
于 2012-11-20T13:07:32.240 回答
8

这个问题是在 2010 年提出的,但在撰写本文时,它是“join file paths in elisp”等搜索的热门搜索,所以我想我会更新答案。

自 2010 年以来,Emacs 的世界发生了翻天覆地的变化。这是一个重复的答案,因为它在下面的答案中被简要提及,但我会稍微充实一下。现在有一个用于文件交互的专用库f.el, :

受到@magnars优秀的s.eldash.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 上可用。

于 2019-06-04T13:15:14.840 回答
4

如果您使用方便的文件和目录操作库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 中的后缀结尾时编写了自己的。

于 2014-03-14T11:43:41.277 回答
4

这是我使用的:

(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可能并不重要。

于 2015-09-27T19:36:26.160 回答
0

对于那些在 2021 年之后提出问题的人。elisp 内置函数file-name-concat可以完成这项工作。现在简单多了。

可以在 emacs 中使用以下按键找到文档:

C-h f file-name-concat <enter>

将 COMPONENTS 附加到 DIRECTORY 并返回结果字符串。

COMPONENTS 中的元素必须是字符串或 nil。COMPONENTS 中的 DIRECTORY 或非最终元素可能以斜杠结尾,也可能不以斜杠结尾——如果它们不以斜杠结尾,则会在连接之前插入斜杠。

其他相关功能记录在文件名组中。
可能在Emacs 版本 28.1 或之前引入。
此函数不会更改全局状态,包括匹配数据。

(file-name-concat "/usr/bin/" "ls")
;; ==> "/usr/bin/ls"

(file-name-concat "/usr" "bin" "ls")
;; ==> "/usr/bin/ls"
于 2022-01-15T13:09:06.277 回答
0

只需通过指向 Emacs 手册的链接来完成之前所说的内容:

正如其他人之前所说,OP 问题的答案是使用expand-file-name. 这是一个内置函数,用 C 语言实现,因此不需要使用任何外部库。

这在Emacs Lisp 手册标题为扩展文件名的函数部分中进行了描述。

并且根据 Emacs 的在线帮助,这个功能是在 Emacs ... 1.6 版本中引入的!所以......它应该可用!

于 2021-05-22T15:45:05.570 回答