In Elisp, how to get path string with slash properly inserted?

*爱你&永不变心* 提交于 2019-11-30 13:08:35

(file-name-as-directory dir) will return directory path dir with a trailing slash, adding one if necessary, and not otherwise.

If you had your sequence of partial paths in a list, you could do something like:

(let ((directory-list '("/foo" "bar/" "p/q/" "x/y"))
      (file-name "some_file.el"))
  (concat
   (mapconcat 'file-name-as-directory directory-list "")
   file-name))

"/foo/bar/p/q/x/y/some_file.el"

or as an alternative, if you wanted to include the file name in the list, you could utilise directory-file-name which does the opposite of file-name-as-directory:

(let ((path-list '("/foo" "bar/" "p/q/" "x/y/some_file.el")))
  (mapconcat 'directory-file-name path-list "/"))

"/foo/bar/p/q/x/y/some_file.el"

(Someone please correct me if using directory-file-name on a non-directory is not portable?)

The easiest way to assemble file names from parts of questionable content is with expand-file-name. For example:

(expand-file-name "foo.txt")

this common form will give you a full file name based on default-directory:

/home/me/foo.txt

but if you have a variable 'dir' whose content is "/home/them/subdir" and want to use that, do this:

(expand-file-name "foo.txt" dir)

it doesn't matter if dir ends in / or not. If you are on some other platform, and contains the other slash, it will do the right thing then too. Do you have a mix? Just stack them:

(expand-file-name "foo.txt" (expand-file-name "somesubdir" dir))

Something like this should work as a starting point, although you'd want to flesh it out a bit to make it platform independent, etc.

(defun append-path-component (path new-part)
  (if (string-match ".*/$" path)
    (concat path new-part)
    (concat path "/" new-part)))

As per usual, there's probably some bit of elisp that already does this that I'm just not aware of.

Unless you really care about keeping relative file names as relative, then it's always much better to avoid concat and use expand-file-name instead.

(defun* tofilename (directorylist &optional (filename nil))
  "concatenate directory names into a path, with an optional file name as last part"
  (concat
   (mapconcat 'directory-file-name directorylist "/")
   "/"
   filename))


(tofilename '("~/" "Temp/") "temp.txt")
;; => "~/Temp/temp.txt"

(tofilename '("~/" "Temp/"))
;; => "~/Temp/"

(tofilename '("~/" "Temp/" "test"))
;; => "~/Temp/temp/"

If you deal with file manipulation, joining and splitting filepaths, checking empty directories and such, I strongly recommend installing f.el, modern file manipulation library. You will have a huge set of file and filepath manipulation functions under one namespace and will never reinvent the wheel again.

The function you need is f-join, it concatenates parts of a path, adding slash only where needed.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!