如何在 Common Lisp 中将子目录附加到路径名

How to append a subdirectory to a pathname in Common Lisp

我在使用 Common Lisp(使用 SBCL)进行路径操作时遇到了一些问题。我正在尝试将子目录名称附加到我拥有的绝对路径名。

示例:我是 运行 我在目录 #P"/home/me/somedir" 中的 repl,我将“otherdir”作为变量,我想要的是 #P"/home/me/somedir/ otherdir

本质上,我试图将我在 Python 中的做法翻译成 Common Lisp:os.path.join(os.getcwd(), "otherdir")

我试过 (merge-pathnames (sb-posix:getcwd) "otherdir/") 但我只是找回了 cwd。如果我尝试 (merge-pathnames "otherdir/" (sb-posix:getcwd)),我会在最后一个目录之前添加 otherdir/:#P"/home/me/otherdir/somedir"

我也尝试过使用 (make-pathname :directory '(:relative "otherdir") :defaults (sb-posix:getcwd)),但我得到的却是#P""otherdir/somedir"。

有谁知道如何在 Common Lisp 中以编程方式建立路径?

>(merge-pathnames "otherdir" #P"/home/me/somedir/")
#P"/home/me/somedir/otherdir"

啊,路径处理之谜......你几乎已经用 merge-pathnames,但第二个参数必须有尾随 /:

(sb-posix:getcwd)
"/home/vince/projets"

=> 没有尾随 /,因此当使用 otherdir/(尾随斜线)或 otherdir:

时我们会得到 2 个意想不到的结果
(merge-pathnames "otherdir/" (sb-posix:getcwd))
#P"/home/vince/otherdir/projets"
;;                      ^^

(merge-pathnames "otherdir" (sb-posix:getcwd))
#P"/home/vince/otherdir"  ;; no "projets"

让我们在右边使用尾随/

(merge-pathnames "otherdir" "/home/vince/projets/")
#P"/home/vince/projets/otherdir"  ;; <= expected

那么还有更“正确”的cwd吗?通常,解决方案由 UIOP 给出(在 ASDF 中提供,因此始终可用)。

TBH我以前不知道,但我查了一下:

(apropos "cwd")
:GETCWD (bound)
OSICAT::CWD
OSICAT-POSIX::%GETCWD (fbound)
OSICAT-POSIX:GETCWD (fbound)
SB-POSIX:GETCWD (fbound)
SB-UNIX:POSIX-GETCWD (fbound)
SB-UNIX:POSIX-GETCWD/ (fbound)
SB-X86-64-ASM::CWD (fbound)
SB-X86-64-ASM::CWDE (fbound)
UIOP/FILESYSTEM::CWD
UIOP/OS:GETCWD (fbound)

(YMMV)

所以:

(UIOP/OS:GETCWD)
#P"/home/vince/projets/"
;;                    ^ yes!

等等,

解决方案

(merge-pathnames "otherdir" (UIOP/OS:GETCWD))
#P"/home/vince/projets/otherdir"

UIOP 是一个可移植的库。 SBCL 的实现是什么?查看来源(M-.):

(sb-ext:parse-native-namestring (sb-unix:posix-getcwd/))

从现有路径构建新路径

注意:对于 Common Lisp UNIX 目录,通常在末尾有一个斜杠。

函数 PATHNAME 解析文件名字符串和 returns 路径名对象。

在 Mac:

上使用 SBCL
CL-USER> (pathname "/Users/foo/bar/")
#P"/Users/foo/bar/"

CL-USER> (merge-pathnames "baz/" *)
#P"/Users/foo/bar/baz/"

CL-USER> (let ((p (pathname "/Users/foo/bar/")))
           (make-pathname :defaults p
                          :directory (append (pathname-directory p)
                                             (list "baz"))))
#P"/Users/foo/bar/baz/"

作为 Lisp 对象的 UNIX 路径名

路径名是一个结构化对象,它也有一个目录组件。其他组件有:devicehostnametype, 版本。路径名对象的绝对目录组件如下所示:

CL-USER > (pathname-directory (pathname "/Users/foo/bar/"))
(:ABSOLUTE "Users" "foo" "bar")

这是一个以关键字 :absolute 开头的列表,后面是字符串形式的目录名称。

如果我们省略尾部斜杠,那么 PATHNAME 函数将以不同方式解析文件名:最后一个组件将是名称:

CL-USER > (pathname-directory (pathname "/Users/foo/bar"))
(:ABSOLUTE "Users" "foo")

CL-USER > (pathname-name (pathname "/Users/foo/bar"))
"bar"