What is the correct way to join multiple path components into a single complete path in emacs lisp?
Reading through the manual for Directory Names, you'll find the answer:
Given a directory name, you can combine it with a relative file name using
concat
:(concat dirname relfile)
Be sure to verify that the file name is relative before doing that. If you use an absolute file name, the results could be syntactically invalid or refer to the wrong file.
If you want to use a directory file name in making such a combination, you must first convert it to a directory name using
file-name-as-directory
:(concat (file-name-as-directory dirfile) relfile)
Don't try concatenating a slash by hand, as in
;;; Wrong! (concat dirfile "/" relfile)
because this is not portable. Always use
file-name-as-directory
.
Other commands that are useful are: file-name-directory
, file-name-nondirectory
, and others in the File Name Components section.
You can use expand-file-name
for this:
(expand-file-name "ls" "/usr/bin")
"/usr/bin/ls"
(expand-file-name "ls" "/usr/bin/")
"/usr/bin/ls"
Edit: this only works with absolute directory names. I think Trey's answer is the preferable solution.
I wanted to join multiple nested directories onto a path. Originally I used multiple expand-file-name
calls, like so:
(expand-file-name "b" (expand-file-name "a" "/tmp"))
"/tmp/a/b"
However this is rather verbose, and reads backwards.
Instead I wrote a function which acts like Python's os.path.join
:
(defun joindirs (root &rest dirs)
"Joins a series of directories together, like Python's os.path.join,
(dotemacs-joindirs \"/tmp\" \"a\" \"b\" \"c\") => /tmp/a/b/c"
(if (not dirs)
root
(apply 'joindirs
(expand-file-name (car dirs) root)
(cdr dirs))))
It works like so:
(joindirs "/tmp" "a" "b")
"/tmp/a/b"
(joindirs "~" ".emacs.d" "src")
"/Users/dbr/.emacs.d/src"
(joindirs "~" ".emacs.d" "~tmp")
"/Users/dbr/.emacs.d/~tmp"