我正在寻找一种方法来移除..并获得一条没有...的路径。 例如,假设存在给定路径
a/b/c/../../1
然后我想从上面得到
a/1
我尝试使用file-truename,但它在给定路径的开头添加了default-directory。
有没有方便的方法?请告诉我。
PS:如果可能,我想避免使用file-truename。
答案 0 :(得分:3)
a/b/c/../../1
实际上并不是一个路径(绝对文件名,在Emacs的说法中)。路径以目录开头。
最多,该模式是相对文件名。相对于什么?在Emacs中,默认为default-directory
。
也许你真正的意思是/a/b/c/../../1
,这是一个绝对的文件名("路径")?如果您使用它,正如您所说,file-truename
可以为您提供所需内容。 expand-file-name
也是如此。
如果您真的想要按a/b/c/../../1
来生成a/1
,那么您可以使用expand-file-name
"/"
作为默认目录参数来执行此操作。或file-truename
喜欢这样:
(let ((default-directory "/"))
(file-truename "a/b/c/../../1"))
您当然可以在命名函数中使用它:
(defun foo (relname)
(let ((default-directory "/"))
(file-truename relname)))
当然,这会为您提供/a/1
,而不是a/1
。如果你真的想要后者,那么只需使用substring
删除第一个/
。
(也许你可以告诉我们你需要的用例。它可能会改变你得到的建议,从而更有帮助。)
答案 1 :(得分:1)
您可以使用纯字符串操作来执行此操作。因此,您不需要使用仅在文件实际存在于文件系统中时才起作用的函数。
下面的函数将字符串拆分为/
:s并遍历结果列表。它会忽略任何.
,只要它看到..
它就会看到已经看到的路径组件。
例如:
(defun my-simplify-path (path)
"Simplify PATH by removing redundant parts.
The operation is only performed using string operations, so PATH
does not have to exist in the file system."
(let ((old-list (split-string path "/"))
(new-list-reversed '()))
(dolist (element old-list)
(cond ((equal element "."))
((and (equal element "..")
(not (null new-list-reversed)))
(pop new-list-reversed))
(t
(push element new-list-reversed))))
(if (null new-list-reversed)
"."
(string-join (nreverse new-list-reversed) "/"))))
以下ERT测试用例演示了此功能的工作原理:
(ert-deftest my-simplify-path-test ()
(should (equal (my-simplify-path "alpha") "alpha"))
(should (equal (my-simplify-path "./alpha") "alpha"))
(should (equal (my-simplify-path "alpha/./beta") "alpha/beta"))
(should (equal (my-simplify-path "alpha/beta") "alpha/beta"))
(should (equal (my-simplify-path "alpha/../beta") "beta"))
(should (equal (my-simplify-path "alpha/beta/gamma/delta/../../..") "alpha"))
(should (equal (my-simplify-path "../alpha") "../alpha"))
(should (equal (my-simplify-path "alpha/../..") ".."))
(should (equal (my-simplify-path "a/b/c/../../1") "a/1")))