在 emacs lisp 中将多个路径组件连接成单个完整路径的正确方法是什么?

发布于 2024-09-27 16:28:02 字数 301 浏览 2 评论 0 原文

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

例如,如果 dir"/usr/bin"file"ls",那么我想要“/usr/bin/ls”。但如果 dir"/usr/bin/",我仍然想要同样的东西,没有重复的斜杠。

Suppose I have variables dir and file containing strings representing a directory and a filename, respectively . What is the proper way in emacs lisp to join them into a full path to the file?

For example, if dir is "/usr/bin" and file is "ls", then I want "/usr/bin/ls". But if instead dir is "/usr/bin/", I still want the same thing, with no repeated slash.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

旧街凉风 2024-10-04 16:28:02

阅读目录名称,你会找到答案:

给定一个目录名称,您可以组合
它与相对文件名使用
连接

(连接目录名relfile)

请务必验证文件名是
在这样做之前相对。如果你使用
绝对文件名,结果
可能在语法上无效或
引用了错误的文件。

如果你想使用目录文件
在进行这样的组合时,你的名字
必须先将其转换为目录
使用文件名作为目录命名:

(concat(文件名作为目录 dirfile)relfile) 

不要尝试
手动连接斜杠,如

<前><代码> ;;;错误的!
(连接目录文件“/”relfile)

因为它不可移植。总是
使用文件名作为目录

其他有用的命令有:file-name-directoryfile-name-nondirectory 以及 文件名组件 部分。

Reading through the manual for Directory Names, you'll find the answer:

Given a directory name, you can combine
it with a relative file name using
concat:

 (concat dirname relfile)

Be sure to verify that the file name is
relative before doing that. If you use
an absolute file name, the results
could be syntactically invalid or
refer to the wrong file.

If you want to use a directory file
name in making such a combination, you
must first convert it to a directory
name using file-name-as-directory:

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

Don't try
concatenating a slash by hand, as in

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

because this is not portable. Always
use file-name-as-directory.

Other commands that are useful are: file-name-directory, file-name-nondirectory, and others in the File Name Components section.

霊感 2024-10-04 16:28:02

您可以使用 expand-file-name 来实现此目的:

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

编辑:这只适用于绝对目录名称。我认为特雷的答案是更好的解决方案。

You can use expand-file-name for this:

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

Edit: this only works with absolute directory names. I think Trey's answer is the preferable solution.

于我来说 2024-10-04 16:28:02

我想将多个嵌套目录加入到一条路径上。最初我使用了多个 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"

I wanted to join multiple nested directories onto a path. Originally I used multiple expand-file-name calls, like so:

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

However this is rather verbose, and reads backwards.

Instead I wrote a function which acts like 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))))

It works like so:

(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"
情话已封尘 2024-10-04 16:28:02

这个问题是在 2010 年提出的,但在撰写本文时,它是“在 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 上可用。

This question was asked in 2010, but at the time of writing it's the top hit for searches like "join file paths in elisp", so I thought I'd update the answer.

Since 2010, things have moved on a lot in the world of Emacs. This is somewhat of a duplicate answer since it was mentioned briefly in an answer below, but I'll flesh it out a little. There's now a dedicated library for file interactions, f.el:

Much inspired by @magnars's excellent s.el and dash.el, f.el is a modern API for working with files and directories in Emacs.

Don't try to reinvent the wheel. You should use this library for file path manipulations. The function you want is f-join:

(f-join "path")                   ;; => "path"
(f-join "path" "to")              ;; => "path/to"
(f-join "/" "path" "to" "heaven") ;; => "/path/to/heaven"

You may need to install the package first. It should be available on MELPA.

静谧幽蓝 2024-10-04 16:28:02

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

可以通过以下按键在 emacs 中找到文档:

Ch f file-name-concat

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

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

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

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

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

For those who come to the question after 2021. elisp builtin function file-name-concat would do the job. It's much simpler now.

Document can be found in emacs with following keystroke:

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

Append COMPONENTS to DIRECTORY and return the resulting string.

Elements in COMPONENTS must be a string or nil.
DIRECTORY or the non-final elements in COMPONENTS may or may not end
with a slash -- if they don't end with a slash, a slash will be
inserted before contatenating.

Other relevant functions are documented in the file-name group.
Probably introduced at or before Emacs version 28.1.
This function does not change global state, including the match data.

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

(file-name-concat "/usr" "bin" "ls")
;; ==> "/usr/bin/ls"
太阳哥哥 2024-10-04 16:28:02

这是我使用的:

(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 将使用以 "/" 开头的 first 组件作为根。

注释

许多文件处理函数(全部、大多数、???)将规范化冗余斜杠并在相对路径上调用 expand-file-name (或类似的),所以 #2而#3 可能并不重要。

Here's what I use:

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

Differences from @dbr's:

  1. Returns an "emacs directory name", i.e. a value with a trailing slash
  2. It does not expand the path if root is relative (see notes)
  3. Treats root as the root, whereas joindirs will use the first component starting with "/" as the root.

Notes

Many file handling functions (all, most, ???) will normalize redundant slashes and call expand-file-name (or similar) on relative paths, so #2 and #3 may not really matter.

心凉 2024-10-04 16:28:02

如果您使用方便的文件和目录操作库 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 完全相同。加入

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 中检查字符串是否以后缀结尾

If you use a convenient file and directory manipulation library f.el, you only need f-join. The below code is for those, who for some reason refuse to use this library.

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

This behaves exactly as Python's os.path.join.

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

string-suffix-p doesn't exist before Emacs 24.4, so i wrote my own at Check if a string ends with a suffix in Emacs Lisp.

信愁 2024-10-04 16:28:02

只是为了通过 Emacs 手册的链接来完成之前所说的内容:

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

Emacs Lisp 手册 标题为 扩展文件名的函数

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

Just to complete what was said before with a link to the Emacs manual:

As others have said before, the answer to the OP question is to use the expand-file-name. That is a built-in function, implemented in C and therefore does not require the use of any external library.

This is described in the Emacs Lisp Manual section titled Functions that Expand Filenames.

And according to Emacs on-line help this function was introduced in version ... 1.6 of Emacs! So... it should be available!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文