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-p
和 directory-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" 名称。
可能这是一个非常愚蠢的问题,但在尝试了所有内置 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-p
和 directory-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" 名称。