Common Lisp:绝对路径的相对路径

Common Lisp: relative path to absolute

可能这是一个非常愚蠢的问题,但在尝试了所有内置 pathname-family 函数和 cl-fad/pathname-utils 包之后我仍然不知道如何将相对路径转换为绝对路径(相对于 $PWD):

; let PWD be "/very/long/way"
(abspath "../road/home"); -> "/very/long/road/home"

假设函数 abspath 的工作方式与 Python 中的 os.path.abspath() 相同。

变量*DEFAULT-PATHNAME-DEFAULTS*通常包含您的初始工作目录,您可以将路径名与其合并;

(defun abspath (pathname)
  (merge-pathnames pathname *default-pathname-defaults*))

并且由于这是 merge-pathnames 的第二个参数的默认值,您可以简单地写:

(defun abspath (pathname)
  (merge-pathnames pathname))

这是最终的解决方案(基于前两个答案):

(defun abspath
       (path-string)
   (uiop:unix-namestring
    (uiop:merge-pathnames*
     (uiop:parse-unix-namestring path-string))))

uiop:parse-unix-namestring 将字符串参数转换为路径名,替换 ... 引用; uiop:merge-pathnames* 将相对路径名转换为绝对路径; uiop:unix-namestring 将路径名转换回字符串。

此外,如果您确定路径指向的文件类型,您可以使用:

(uiop:unix-namestring (uiop:file-exists-p path))

(uiop:unix-namestring (uiop:directory-exists-p path))

因为 file-exists-pdirectory-exists-p return 绝对路径名(或 nil,如果文件不存在)。

更新:

显然在某些实现中(如 ManKai Common Lisp)如果给定的路径名​​缺少 ./ 前缀(例如如果你喂它 #P"main.c" 而不是 #P"./main.c")。所以更安全的解决方案是:

(defun abspath
       (path-string &optional (dir-name (uiop:getcwd)))
   (uiop:unix-namestring
    (uiop:ensure-absolute-pathname
     (uiop:merge-pathnames*
      (uiop:parse-unix-namestring path-string))
     dir-name)))

UIOP

这是 UIOP 的文档中关于 cl-fad 的内容:-)

UIOP completely replaces it with better design and implementation

UIOP 附带了大量实现(由 ASDF3 使用),因此当您需要时它基本上已经可用(请参阅文档中的 "Using UIOP"。) .库中定义的众多函数之一是 uiop:parse-unix-namestring,它理解 Unix 文件名的语法而不检查路径是否指定现有文件或目录。但是,双点被解析为 :back:up,您的实现不一定支持。使用 SBCL,情况就是这样,路径也得到了简化。请注意,路径名允许同时使用 :back:up 组件; :back 只看路径名就可以简化(它是语法上的目录),而 :up 是语义上的目录,这意味着它取决于实际的文件系统。如果文件名存在,您将有更好的机会获得规范的文件名。

真名

您也可以调用 TRUENAME, which will probably get rid of the ".." components in your path. See also 20.1.3 Truenames,这说明您可以使用不同的路径名指向同一个文件,但通常只有一个 "canonical" 名称。