What is the correct way to join multiple path components into a single complete path in emacs lisp?

Viewed 16525

Suppose I have variables dir and file containing strings representing a directory and a filename, respectively . What is the proper way in emacs lisp to join them into a full path to the file?

For example, if dir is "/usr/bin" and file is "ls", then I want "/usr/bin/ls". But if instead dir is "/usr/bin/", I still want the same thing, with no repeated slash.

8 Answers

This question was asked in 2010, but at the time of writing it's the top hit for searches like "join file paths in elisp", so I thought I'd update the answer.

Since 2010, things have moved on a lot in the world of Emacs. This is somewhat of a duplicate answer since it was mentioned briefly in an answer below, but I'll flesh it out a little. There's now a dedicated library for file interactions, f.el:

Much inspired by @magnars's excellent s.el and dash.el, f.el is a modern API for working with files and directories in Emacs.

Don't try to reinvent the wheel. You should use this library for file path manipulations. The function you want is f-join:

(f-join "path")                   ;; => "path"
(f-join "path" "to")              ;; => "path/to"
(f-join "/" "path" "to" "heaven") ;; => "/path/to/heaven"

You may need to install the package first. It should be available on MELPA.

Just to complete what was said before with a link to the Emacs manual:

As others have said before, the answer to the OP question is to use the expand-file-name. That is a built-in function, implemented in C and therefore does not require the use of any external library.

This is described in the Emacs Lisp Manual section titled Functions that Expand Filenames.

And according to Emacs on-line help this function was introduced in version ... 1.6 of Emacs! So... it should be available!

For those who come to the question after 2021. elisp builtin function file-name-concat would do the job. It's much simpler now.

Document can be found in emacs with following keystroke:

C-h f file-name-concat <enter>

Append COMPONENTS to DIRECTORY and return the resulting string.

Elements in COMPONENTS must be a string or nil. DIRECTORY or the non-final elements in COMPONENTS may or may not end with a slash -- if they don't end with a slash, a slash will be inserted before contatenating.

Other relevant functions are documented in the file-name group.
Probably introduced at or before Emacs version 28.1.
This function does not change global state, including the match data.

(file-name-concat "/usr/bin/" "ls")
;; ==> "/usr/bin/ls"

(file-name-concat "/usr" "bin" "ls")
;; ==> "/usr/bin/ls"
Related