44

I have seen examples online where people add paths to the default path in Emacs with:

(add-to-list 'exec-path "/usr/local/bin/")

I am new to Elisp, and I think I understand what the statement above does, but I have a few questions:

  • In what order does Emacs search through the execution paths? For example, does it consider the value of $PATH (env. variable) at all (and if so, before or after exec-path?)

  • How can I prepend multiple such paths? Can I just keep concatenating them? e.g.

    (add-to-list 'exec-path "PATH1", "PATH2")
    

    or should I do:

    (add-to-list 'exec-path "PATH1:PATH2:PATH3") 
    

I also found this interesting package on GitHub: exec-path-from-shell. Why is there a need for a package for this?

Motivation

Ever find that a command works in your shell, but not in Emacs?

This happens a lot on OS X, where an Emacs instance started from the GUI inherits a default set of environment variables.

This library works solves this problem by copying important environment variables from the user's shell: it works by asking your shell to print out the variables of interest, then copying them into the Emacs environment.

3
  • 5
    Welcome to Emacs and Elisp! I'm still pretty fresh with the topic myself and don't know the answers to your questions, but I thought I'd mention something that has made life a heck of a lot easier: the describe functions. e.g. (describe-function 'add-to-list) (C-h f) will give you the doc for the add-to-list function, as well as links to the source. There's also (describe-variable 'exec-path) (C-h v). This isn't meant to be a RTFM comment -- these docs don't answer all the questions you listed, just something useful.
    – jtmoulia
    Oct 1, 2014 at 14:51
  • Thanks @jtmoulia! I will definitely keep that in mind for future questions. Oct 1, 2014 at 15:29
  • In addition to C-h v exec-path, use the manual(s) (Emacs and Elisp). In a manual, i exec-path directs you to a helpful explanation. Ask Emacs first - you won't be sorry you did.
    – Drew
    Oct 14, 2014 at 15:19

3 Answers 3

38

1) PATH and exec-path

Emacs does set exec-path from the value of PATH on startup, but will not look at it again later. But if you run a command, it will inherit PATH, not exec-path, so subprocesses can find different commands than Emacs does.

As Francesco says, this can be especially confusing for shell-command, as that does not run a process directly, but calls a shell to run it, which will use PATH, not exec-path.

2) Adding multiple paths to exec-path

Just call add-to-list repeatedly:

(add-to-list 'exec-path "PATH1")
(add-to-list 'exec-path "PATH2")

Do note that add-to-list adds to the beginning of the list, so this will end up with "PATH2" being in the exec-path before "PATH1".

You can also use more "low-level" access to lists:

(setq exec-path (append '("PATH1" "PATH2")
                        exec-path))

This will add "PATH1" and "PATH2" to your exec-path, in that order.

3) Mac OS' PATH

The problem on Mac OS X is that Mac OS does not set the environment the same when you call a program from the global UI or when you call it from a shell. This means that running Emacs from a shell will result in different environment variables being set than when you run it from the finder. This is especially annoying if you set environment variables in .bashrc or similar, as that won't affect the "global" Emacs.

The package apparently starts a shell and imports environment variables from there, mimicing the environment you get from a shell in a globally-started Emacs.

4
  • 1
    Thanks Jorgen. Regarding Q3: Do you happen to know where the default PATH is defined for programs started from the global UI (desktop environment)? I am having the same problem with PYTHONPATH in elpy :). When I start Emacs from the desktop, Emacs is not aware of my PYTHONPATH definitions in my .zshenv file (an init file for zsh), which is very frustrating, since elpy does not know then where to find my Python packages. I am happy to move these PYTHONPATH definitions to a different shell init file (although ideally I would like Emacs to use the definitions from my .zshenv) Oct 1, 2014 at 15:38
  • I am afraid I do not know much at all about Mac OS X. A quick google got me to stackoverflow.com/questions/135688/… which seems to indicate that launchd.conf is the right place. If all you need this for is Emacs, you can of course just set both exec-path and PATH in your .emacs. You can set PATH using (setenv "PATH" (format "%s:%s" "/new/path/element" (getenv "PATH"))). Oct 1, 2014 at 18:17
  • Thanks. I know the following Q diverges a bit from the OP, but is there any way to specify in Emacs the PYTHONPATH that elpy should use? Oct 1, 2014 at 18:25
  • You can modify process-environment, for example using setenv. You can do that in elpy-mode-hook, but it is a globsl variable and making it buffer-local can easily lead to confusing behavior. Oct 1, 2014 at 21:41
9

When emacs starts a new external process using primitive functions such as call-process or start-process, the executable is searched in exec-path (and not $PATH)

However, a function such as shell-command starts the shell as a subprocess and passes it the command you want to run. In order to execute this command, the shell will then try to find the executable in $PATH (and not in exec-path).

Therefore, exec-path is what counts most for external processes which are started by emacs itself, whereas $PATH is what counts for commands you run yourself with higher-level function (using M-! for example)


If you want to add multiple directories to exec-path, you should use add-to-list multiple times.

You can do it either manually

(add-to-list 'exec-path "dir1")
(add-to-list 'exec-path "dir2")

or using a loop

(dolist (dir '("dir1" "dir2"))
  (add-to-list 'exec-path dir))

Regarding your third question, if emacs was launched from the desktop environment, it inherits the environment from it, which might be less complete than that of a full shell.

This means that it might sometimes be necessary to complete Emacs' value for $PATH using what a regular shell sees. This is the purpose of the exec-path-from-shell library you mention.

2

I tested the following solution on Mac 12.4. It works.

https://www.emacswiki.org/emacs/ExecPath

(defun set-exec-path-from-shell-PATH ()
  "Set up Emacs' `exec-path' and PATH environment variable to match
that used by the user's shell.

This is particularly useful under Mac OS X and macOS, where GUI
apps are not started from a shell."
  (interactive)
  (let ((path-from-shell (replace-regexp-in-string
              "[ \t\n]*$" "" (shell-command-to-string
                      "$SHELL --login -c 'echo $PATH'"
                            ))))
    (setenv "PATH" path-from-shell)
    (setq exec-path (split-string path-from-shell path-separator))))

(set-exec-path-from-shell-PATH)
1
  • You can remove the replace-regexp-in-string call by running echo with the -n option (which won't print a new line, so you won't have to remove that).
    – Renato
    Dec 10, 2022 at 19:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.