Setting up Emacs shell on a Mac

Here are a few things I’ve had to figure out in the process of setting up Emacs on a Mac, in particular with getting shell-mode to work as I’d like. Maybe this will save someone else some time if they want to do the same.

I’ve used a Mac occasionally since the days of the beige toasters, but I never owned one until recently. I’ve said for years that I’d buy a Mac as soon as I have a justification, and I recently started a project that needs a Mac.

I’d heard that Emacs was hard to set up on Mac, but that has not been my experience. I’m running Emacs 25.1 on macOS 10.12.1. Maybe there were problems with earlier versions of Emacs or OS X that I skipped. Or maybe there are quirks I haven’t run into yet. So far my only difficulties have been related to running a shell inside Emacs.

Path differences

The first problem I ran into is that my path is not the same inside shell-mode as in a terminal window. A little searching showed a lot of discussion of this problem but no good solutions. My current solution is to run source .bash_profile from my bash shell inside Emacs to manually force it to read the configuration file. There’s probably a way to avoid this, and if you know how please tell me, but this works OK for now.

Manually sourcing the .bash_profile file works for bash but doesn’t work for Eshell. I doubt I’ll have much use for Eshell, however. It’s more useful on Windows when you want a Unix-like shell inside Emacs.

Update: Dan Schmidt pointed out in the comments that Emacs reads .bashrc rather than .bash_profile. It seems that Mac doesn’t read .bashrc at all, at least not if it can find a .bash_profile file. I created a .bashrc file that sources .bash_profile and that fixed my problem, though it did not fix the problem with Eshell or the path problem below.

Scrolling command history

The second problem I had was that Control-up arrow does not scroll through shell history because that key combination has special meaning to the operating system, bringing up Mission Control. Quite a surprise when you expect to scroll through previous commands but instead your entire screen changes.

I got around this by putting the following code in my Emacs config file and using Alt-up and Alt-down instead of Control-up and Control-down to scroll shell history. (I’m using my beloved Microsoft Natural keyboard, so I have an Alt key.)

(add-hook 'shell-mode-hook
  (lambda ()
    (define-key shell-mode-map (kbd "<M-up>") 'comint-previous-input)
    (define-key shell-mode-map (kbd "<M-down>") 'comint-next-input)
  )
)

Another path problem

The last problem I had was running the Clojure REPL inside Emacs. When I ran lein repl from bash inside Emacs I got an error saying command not found. Apparently running source .bash_profile didn’t give me entirely the same path in Emacs as in a terminal. I was able to fix the following to my Emacs config file.

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

This works, though there are a couple things I don’t understand. First, I don’t understand why /usr/local/bin was missing from my path inside Emacs. Second, I don’t understand why adding the path customizations from my .bash_profile to exec-path doesn’t work. Until I need to understand this, I’m willing to let it remain a mystery.

Update: LaTeX path problem

After fixing the problems mentioned in the original post, I ran into another problem. Trying to run LaTeX on a file failed saying that pdflatex couldn’t be found. Adding the path to pdflatex to the exec-path didn’t work. But the following code from the TeX Stack Exchange did work:

(getenv "PATH")
(setenv "PATH" (concat "/Library/TeX/texbin" ":" (getenv "PATH")))

This is the path for El Capitan and Sierra. The path is different in earlier versions of the OS.

Portable Emacs config file

By the way, you can use one configuration file across operating systems by putting code like this in your file.

(cond
    ((string-equal system-type "windows-nt") 
        (progn
            ; Windows-specific configurations
            ...
        )
    )
    ((string-equal system-type "gnu/linux")
        (progn
            ; Linux-specific configurations
            ...
        )
    )
    ((string-equal system-type "darwin")
        (progn
            ; Mac-specific configurations
            ...
        )
    )
)

If you need machine-specific configuration for two machines running the same OS, you can test system-name rather than system-type.

5 thoughts on “Setting up Emacs shell on a Mac

  1. It sounds like you bumped into the login/start file world. I initially noticed it with ssh, but it’s clearly the same issue.
    By default, bash will read .bash_profile (or .profile if .bash_profile doesn’t exist) *only* for a login shell. Additional shells will then *only* read .bashrc .
    In practice, this means that .bash_profile gets read once, when you log in, and then .bashrc gets read for each terminal/emacs shell window after that.
    For that reason, my login files look like:
    .bash_profile….
    [ -e ~/.profile] && source ~/.profile

    export _BASH_PROFILE_READ=1
    [ -e ~/.bashrc -a -t 0 ] && source ~/.bashrc

    .bashrc
    [ -z “$_BASH_PROFILE_READ” -a -e ~/.bash_profile ] && source ~/.bash_profile
    if [ -z “$_BASHRC_READ ]; then

    export _BASHRC_READ=1
    fi

  2. Christian Lynbech

    Although I am not an expert on the inner workings of macOS vs shell customisations, here a few things to ponder.

    There is a difference between macOS and Linux in that in Linux, you always run things through a shell, one way or the other. In macOS, if you start emacs via Finder (e.g. by clicking on the icon), you only have whatever initialisation macOS did when starting up.

    However, if you are running a shell inside of Emacs, that shell will by itself pick up the normal shell initialisation files as part of its own startup.

    The variable exec-path is used by emacs for the processes it starts itself. So exec-path is important for an inferior shell run in shell-mode (Emacs needs to be able to find that) but will not by itself do anything to the environment of the shell process. However, exec-path is most likely initialized from the PATH environment variable so it will look rather similar to what a shell will see when it starts ups (without any PATH manipulation). That is also one reason why, to find programs in strange places, one needs to manipulate exec-path rather than changing PATH (Emacs does support both getenv and setenv).

  3. Christian Lynbech

    One might add that there are several options for how to run shells inside of emacs.

    The command `eshell’ starts up a shell in which a number of commands are implemented inside of elisp which makes it much better at (for instance) tracking the current directory. It does have (or at least used) problems when running things in the shell that are themselves shell like since it does not do the same kind of prompt tracking that shell-mode. Although I have not experimented much with that, I believe that eshell works well together with tramp, with is a solution for accessing files and commands on remote hosts in a variety of ways (such as ssh); for oldtimers, this is like ange-ftp on steroids.

    Another option is `ansi-term’ which tries to provide a rather complete ANSI terminal emulation which means it is much better at interacting with programs that expect more complex terminal interaction. It has two modes, a line mode that feels very much like shell-mode and character mode in which characters are piped directly to the inferior shell such that `up’ is interpreted by the shell and gets you the previous command, as seen by the shell, where shell-mode has `meta-up’ that gives you the last command you entered through to Emacs.

    And then there are specialised solutions like SLIME which is incredible smart at interacting with an inferior lisp and I do believe clojure is supported, though I have not worked much with that.

    I have myself shifted to eshell as my main shell driver, in part because I run on many different kinds of systems and this gives a rather uniform experience across these.

Leave a Reply

Your email address will not be published.