13

This question has been asked at superuser is-there-a-way-to-reload-environment-variables-in-emacs, but no good solution was given.

I am using EmacsClient with often more than 30 buffers open, if I change an environment variable in the shell, I need to exit EmacsClient ( and reopen all the buffers) or I have to manually set the environment variable also in Emacs. I find it annoying that I cannot update the environment variables easily in Emacs. Any suggestions?

Håkon Hægland
  • 3,608
  • 1
  • 20
  • 51
  • There is no straightforward way to do this, because changing an environment variable in the parent process will not update its value as exported to a child. – Erik Hetzner Jun 17 '15 at 18:33

3 Answers3

9

exec-path-from-shell provides the exec-path-from-shell-copy-env command, which lets you copy the value of environment variables to Emacs sessions. For instance, M-x exec-path-from-shell-copy-env RET FOO sets the value of $FOO in Emacs as well.

Note that exec-path-from-shell-copy-env spawns a new shell to extract the value of the environment variable. Hence it will only work for variables that you set in your shell configuration files (e.g. .bashrc), but not for variables only set in a running shell session with export. Extracting these variables is generally impossible without convoluted hacks that inspect /proc/ or similar API for running processes.

6

As a workaround, the following can be used (Linux, Bash):

  • First run printenv -0 > env.txt from the Bash terminal window,
  • Then from within Emacs, run
(defun my-update-env ()
  (interactive)
  (let ((str 
         (with-temp-buffer
           (insert-file-contents "env.txt")
           (buffer-string))) lst)
    (setq lst (split-string str "\000"))
    (while lst
      (setq cur (car lst))
      (when (string-match "^\\(.*?\\)=\\(.*\\)" cur)
        (setq var (match-string 1 cur))
        (setq value (match-string 2 cur))
        (setenv var value))
      (setq lst (cdr lst)))))

Update

I turns out that this can be done more elegantly using the --eval option of the emacsclient command: Define a Bash script update_emacs_env:

#! /bin/bash

fn=tempfile
printenv -0 > "$fn" 
emacsclient -s server_name -e '(my-update-env "'"$fn"'")' >/dev/null

where server_name is your Emacs server name, and my-update-env is a function defined by your ~/.emacs file:

(defun my-update-env (fn)
  (let ((str 
         (with-temp-buffer
           (insert-file-contents fn)
           (buffer-string))) lst)
    (setq lst (split-string str "\000"))
    (while lst
      (setq cur (car lst))
      (when (string-match "^\\(.*?\\)=\\(.*\\)" cur)
        (setq var (match-string 1 cur))
        (setq value (match-string 2 cur))
        (setenv var value))
      (setq lst (cdr lst)))))

Now you can simply type update_emacs_env from the shell command line to update the Emacs environment variables..

Håkon Hægland
  • 3,608
  • 1
  • 20
  • 51
3

I used to use this:

function export-emacs {
    if [ "$(emacsclient -e t)" != 't' ]; then
        return 1
    fi

    for name in "${@}"; do
        value=$(eval echo \"\$${name}\")
        emacsclient -e "(setenv \"${name}\" \"${value}\")" >/dev/null
    done
}

Lets you export a named variable, E.G:

export EDITOR=vim
export-emacs EDITOR
Scott Weldon
  • 2,695
  • 1
  • 17
  • 31
Phil Jackson
  • 131
  • 3