38

In my shell environment I have aliases and custom functions. When I am in an instance of emacs (I always use emacs -nw) and I execute a shell command (M-!) I cannot use them. This makes sense since I imagine it launches it's own subshell to do these... but is there a way (maybe in my .emacs) to get this to work? Perhaps even if it involved sourcing an environment by default before executing any shell command given?

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
Palace Chan
  • 8,845
  • 11
  • 41
  • 93
  • 1
    It's starting in non-login mode, you'll have to find where to change the command to run to start the shell and add `-l`. I use vim so I'm not sure where emacs hides this. – Kevin Sep 01 '12 at 04:39
  • The problem is not login but interactive. The execute subcommand shell is non interactive (as shown by running `M-x ! tty -s` which gives a code of 1). – Nicolas Dudebout Sep 01 '12 at 14:43
  • possible duplicate of [Emacs compilation mode wont see bash alias](http://stackoverflow.com/questions/10946219/emacs-compilation-mode-wont-see-bash-alias) – UpAndAdam Sep 12 '13 at 16:55
  • I had this issue as well. However, due to the peculiarities of `M-&` and its counterpart in Dired, I just gave up and put my alias into `.zshenv` instead. [See my answer on this question for more details.](https://emacs.stackexchange.com/a/53554/10761) – GDP2 Nov 05 '19 at 04:06

6 Answers6

44

Below are my comments about what I think was a related question:

I think both M-x shell-command and M-x compile execute commands in an inferior shell via call-process. Try the following in your .emacs (or just evaluate):

(setq shell-file-name "bash")
(setq shell-command-switch "-ic")

I notice that after evaluation of the above, .bashrc aliases are picked up for use by both M-x shell-command and M-x compile, i.e

M-x compile RET your_alias RET

should then work.

My environment: Emacs 24.1 (pretest rc1), OSX 10.7.3

Source

Community
  • 1
  • 1
Keith Flower
  • 4,032
  • 25
  • 16
  • This worked beautifully! Thanks, what does the "-ic" part do? Is it for interactive? – Palace Chan Sep 01 '12 at 21:56
  • 2
    @PalaceChan: `-i` is for interactive, and `-c` tells bash to read whatever commands follow (so whatever Emacs uses in it's shell call). – jmdeldin Sep 01 '12 at 23:51
  • 2
    @PalaceChan given your next question ( http://stackoverflow.com/questions/15918614/how-to-run-shell-commands-in-emacs-on-ubuntu-while-avoiding-a-bash-job-control-e?lq=1 ) which asks why this gives you an error how did it work beautifully? – UpAndAdam Sep 12 '13 at 17:00
  • 2
    (Emacs 24.4.1 in Terminator) This is pretty much working for me but I get messages before output: bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell – AAAfarmclub Jun 26 '17 at 04:58
  • 1
    This doesn't work - I'm running emacs 26.1, bash 4.4 – fbynite Jan 03 '19 at 18:01
  • Worked. But got warnings `bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell` – updogliu Mar 31 '21 at 13:45
  • Works for me with `GNU Emacs 27.1`, `zsh 5.8 (x86_64-apple-darwin20.0)` (**not** changing `shell-file-name`, just `shell-command-switch`) – ijoseph Apr 09 '21 at 19:45
  • Worked for me and changed my life!, a bit, running Aquamacs 3.5 -> GNU Emacs 25.3.50. – Tom Swirly Jun 14 '21 at 12:13
2

Have a read through http://www.gnu.org/software/bash/manual/bashref.html#Bash-Startup-Files

For non-interactive shells, the only file that is sourced is the value of the BASH_ENV environment variable. You invoke emacs like BASH_ENV=~/.bashrc emacs if emacs will use bash for shell commands -- some programs specifically use "/bin/sh"

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 2
    It seems that you will also have to enable the alias mechanism by using `shopt -s expand_aliases`. This is not enabled in a non interactive shell by default. – Nicolas Dudebout Sep 01 '12 at 14:48
2

To get shell-command to read ~/.bashrc before executing the command per the technique described in https://stackoverflow.com/a/12228646/8869495, without potential side effects due to $BASH_ENV being defined for your entire Emacs session, try this in your ~/.emacs.d/init.el (or .emacs) file:

;; I want M-! (shell-command) to pick up my aliases and so run .bashrc:
(setq shell-file-name "bash-for-emacs.sh")  ; Does this: BASH_ENV="~/.bashrc" exec bash "$@"
(setq shell-command-switch "-c")

As described by the comment in the second line, also install (in your $PATH) a script named bash-for-emacs.sh that contains:

#!/bin/bash

BASH_ENV="~/.bashrc" exec bash "$@"

Note that your ~/.bashrc might need to be changed to define non-interactive aliases even when [ -z "$PS1" ] is true (which indicates a non-interactive shell). That's the approach I'm taking for my own environment (such as it is), which is at https://github.com/jcburley/UnixHome.

Also, this assumes you want to use Bash as your Emacs-spawned shell, as I do.

2

We need to enable alias expansion in non-interactive shells, preferably only when they are called from emacs (to avoid causing subtle differences when we run other shells). What worked for me was to create two files: ~/.alias has all my aliases, e.g.

alias python python27

and ~/.aliasExpand has

source ~/.alias
shopt -s expand_aliases

I also, of course, replaced all my aliases in .bashrc with

source ~/.alias

Finally, I added this to my .emacs:

(setenv "BASH_ENV" "~/.aliasExpand")

I tried Keith Flower's solution (of making shells interactive), and I got

bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell

Note that I got this through Glenn Jackman's and Nicholas Dudebout's hints.

If you are willing to risk having all your shells have alias expansion and you don't mind running all your .bashrc for every emacs shell, you can simplify this to adding

shopt -s expand_aliases

to your .bashrc and

(setenv "BASH_ENV" "~/.bashrc")

to your .emacs

Ethan Bradford
  • 710
  • 8
  • 10
  • 1
    Thanks for the answer. BTW, I think you can just use `(setenv "BASH_ENV" (expand-file-name "~/.bashrc"))` or `(setenv "BASH_ENV" (substitute-in-file-name "$HOME/.bashrc"))`, then `shopt` won't be necessary. – fengqi Dec 13 '19 at 03:13
1

Setting shell-command-switch directly will substantially make shell-command much more sluggish (if you use many (ba|z)sh plugins), which is invoked by various non-interactive functions like shell-command-to-string, eshell-remote-command,... and numerous calls of it from other functions, all of which won't ever need .bashrc or .zshrc

For example, on my machine (and likely yours, too):

(benchmark-elapse (let ((shell-command-switch "-c")) (shell-command "")))
0.005170422
(benchmark-elapse (let ((shell-command-switch "-ic")) (shell-command "")))
0.242628824
;; Empty ~/.zshrc: 0.168341049

Therefore, we should opt find a way to use the interactive shell only when shell-command is called interactively

;;;###autoload
(defun my-shell-command-interactive-a (fn &rest args)
  "Use interactive shell for `shell-command' when invoked interactively.
Setting the \"-i\" switch all the time will significantly slow
down `shell-command' because there may too many files to source."
  (let ((shell-command-switch (if (called-interactively-p 'interactive)
                                  ;; Replace the first "-"
                                  (replace-regexp-in-string
                                   "\\(-\\).*\\'"
                                   "-i"
                                   shell-command-switch
                                   nil
                                   nil
                                   1)
                                shell-command-switch)))
    (apply fn args)))

(dolist (cmd '(shell-command async-shell-command)) 
   (advice-add cmd :around #'my-shell-command-interactive-a))
Daanturo
  • 124
  • 2
  • 7
-3

Put the aliases and functions in .bashrc, not .bash_profile. The latter is only executed in login shells, the former is in all shells.

Barmar
  • 741,623
  • 53
  • 500
  • 612