Difference between revisions of "Shell"

From WikEmacs
Jump to navigation Jump to search
(Created page with "'''Shell''' is a command-line interpreter that provides text user interface for an Operating System. User can use shell inside Emacs, with '''shell mode''', '''eshe...")
 
(add category)
 
(43 intermediate revisions by 8 users not shown)
Line 1: Line 1:
'''Shell''' is a command-line interpreter that provides text [[user interface]] for an [[Operating System]].  
+
'''Shell-mode''' gives access to a shell in a normal emacs buffer, meaning you can move around and edit it as usual. A drawback is that you can not launch programs like htop or other ncurses ones, like you would in term-mode (however, there are often built-in alternatives).
  
 +
= Usage =
  
User can use shell inside Emacs, with '''shell mode''', '''eshell''' or '''term'''.
+
== basics ==
  
== shell mode ==
+
{{Command|shell}}
in this mode, Emacs run an inferior shell, with I/O through [[buffer]] (default to *shell*).
 
  
== eshell ==
+
Look at the menu: you have several keys to interact with the shell. Some of them are:
in this mode, Emacs emulate a shell. It is writen by Emacs Lisp.
 
  
== term ==
+
* {{Keys|M-p}} previous input of command line
in this mode, Emacs emulate a terminal, then Emacs create a shell that you choose with the terminal.
+
* {{Keys|M-r}} search backward a regexp in commands' history (like C-r in term). Use C-r to cycle.
 +
* {{Keys|C-c r}} go to beginning of output (useful when you have a large output and want to read through the beginning)
 +
* {{Keys|C-c-p}} go to beginning of previous output group
 +
* {{Keys|C-c-c}} send the '''C-c''' command to the shell
 +
* {{Keys|C-c-o}} delete the output of the last command
 +
 
 +
To launch a shell in the directory of the current buffer, have a look to '''shell-here''': https://github.com/ieure/shell-here (available through ELPA).
 +
 
 +
== re-execute successive commands ==
 +
 
 +
Often it is useful to reexecute several successive shell commands that were previously executed in sequence. To do this, first find and reexecute the first command of the sequence. Then type {{Keys|C-c C-x}}, that will fetch the following command--the one that follows the command you just repeated. Then type RET to reexecute this command. You can reexecute several successive commands by typing {{Keys|C-c C-x RET}} over and over.
 +
 
 +
= Customisation =
 +
 
 +
== Fix, add colors and highlight text ==
 +
 
 +
If you have bad colors in the output, try using '''ansi-mode''':
 +
 
 +
<source lang="lisp">
 +
(require 'ansi-color)
 +
(defun colorize-compilation-buffer ()
 +
  (toggle-read-only)
 +
  (ansi-color-apply-on-region (point-min) (point-max))
 +
  (toggle-read-only))
 +
(add-hook 'compilation-filter-hook 'colorize-compilation-buffer)
 +
</source>
 +
 
 +
You can highlight some text based on regexp (useful to see "OK" or warnings):
 +
 
 +
<source lang="lisp">
 +
(add-hook 'shell-mode-hook (lambda () (highlight-regexp "\\[OK\\]" "hi-green-b")))
 +
</source>
 +
 
 +
== Make URLs clickable ==
 +
 
 +
<source lang="lisp">
 +
(add-hook 'shell-mode-hook (lambda () (goto-address-mode )))
 +
</source>
 +
 
 +
== Make file paths clickable ==
 +
 
 +
Every line representing a path to a file will be colorized and made clickable, so that you can jump to that file and that line, like in compilation-mode (specially useful when compiling a program or running tests):
 +
 
 +
<source lang="lisp">
 +
(add-hook 'shell-mode-hook 'compilation-shell-minor-mode)
 +
</source>
 +
 
 +
Now you can use key bindings from the mode: use {{CommandKeys|C-x `|next-error}} (backquote) to go to the next error detected in the shell. You can't do that in an xterm !
 +
 
 +
== Shell completion with a nice menu à la zsh ==
 +
 
 +
So you are in shell-mode and you are annoyed that emacs opens a new window with the list of completions when you press TAB and you like the pop-up menu that zsh offers ? The solution is to ask [http://company-mode.github.io/ company-mode] to do the shell completion. It has a built-in backend ''company-capf'' which fetches completion candidates from emacs' ''completion-at-point-functions'' (the same mechanism that powers completion in the shell) as such completion offered by company is as accurate as the built-in shell-mode while making the user interface very convenient. We can activate it with:
 +
 
 +
<source lang="lisp">
 +
(add-hook 'shell-mode-hook #'company-mode)
 +
(define-key shell-mode-map (kbd "TAB") #'company-manual-begin))
 +
</source>
 +
 
 +
And the result:
 +
 
 +
[[File:emacs-shell-company.png]]
 +
 
 +
== Shell completion with helm ==
 +
 
 +
Please see [https://github.com/emacs-helm/helm/wiki#completion-in-emacs-shell the helm doc].
 +
 
 +
== Change directory with ido-mode ==
 +
 
 +
Using [[ido]]'s completion system for changing directories is a big gain in usability and efficiency for emacs' shell. The little package [https://gitlab.com/emacs-stuff/fasd-shell fasd-shell] allows to use the '''fasd''' shell utility in conjonction with '''ido-completion''' to effectively change directories. Its code comes from this Stack Overflow question: http://stackoverflow.com/questions/20952995/emacs-shell-change-directory-with-ido
 +
 
 +
== Shared and persistent history ==
 +
 
 +
By default a shell session inside emacs via '''shell-mode''' won't persist accross sessions and won't read your shell's history. The '''history''' shell command works as expected, but you can't search (with '''M-r''') a command that you typed in shell-mode in another emacs instance or in a term. The following fixes that.
 +
 
 +
For '''comint-mode''' and derivatives (including '''shell-mode''') the searchable history is read in via '''comint-read-input-ring''', which uses '''comint-input-ring-file-name''' which you can set in a mode hook. However I would suggest that you actually set your HISTFILE environment variable to ~/.zsh_history because shell-mode automatically defers to that.
 +
 
 +
The documentation suggests that:
 +
 
 +
<source lang="lisp">
 +
(add-hook 'shell-mode-hook 'my-shell-mode-hook)
 +
(defun my-shell-mode-hook ()
 +
  (setq comint-input-ring-file-name "~/.zsh_history")  ;; or bash_history
 +
  (comint-read-input-ring t))
 +
</source>
 +
 
 +
Note that if you want to persist more variables (mini-buffer history,…), enable '''savehist-mode''' and configure the variables you wish to persist between sessions. See {{Command|customize-group RET savehist RET}}.
 +
 
 +
== Search the bash, zsh or fish history with Ivy-mode ==
 +
 
 +
Again a code snippet from [http://blog.binchen.org/posts/use-ivy-mode-to-search-bash-history.html Redguardto's blog]. This snippet defines a function "counsel-yank-bash-history" that one call manually. We are presented an interactive minibuffer (with ivy, à la ido) with all the history. The line we select is put in the kill ring, ready to be pasted.
 +
 
 +
You can find adaptation for zsh below and [https://gist.github.com/heikkil/177ff4c4b8a02d574958 here for fish].
 +
 
 +
<source lang="lisp">
 +
(defun counsel-yank-bash-history ()
 +
  "Yank the bash history"
 +
  (interactive)
 +
  (let (hist-cmd collection val)
 +
    (shell-command "history -r") ; reload history
 +
    (setq collection
 +
          (nreverse
 +
          (split-string (with-temp-buffer (insert-file-contents (file-truename "~/.bash_history"))
 +
                                          (buffer-string))
 +
                        "\n"
 +
                        t)))
 +
    (when (and collection (> (length collection) 0)
 +
              (setq val (if (= 1 (length collection)) (car collection)
 +
                          (ivy-read (format "Bash history:") collection))))
 +
        (kill-new val)
 +
        (message "%s => kill-ring" val))))
 +
</source>
 +
 
 +
Here is a slight adaptation for zsh:
 +
 
 +
<source lang="lisp">
 +
(defun counsel-yank-zsh-history ()
 +
  "Yank the zsh history"
 +
  (interactive)
 +
  (let (hist-cmd collection val)
 +
    (shell-command "history -r") ; reload history
 +
    (setq collection
 +
          (nreverse
 +
          (split-string (with-temp-buffer (insert-file-contents (file-truename "~/.zsh_history"))
 +
                                          (buffer-string))
 +
                        "\n"
 +
                        t)))
 +
 
 +
    (setq collection (mapcar (lambda (it) (replace-regexp-in-string ".*;" "" it)) collection)) ;; for zsh
 +
                     
 +
    (when (and collection (> (length collection) 0)
 +
              (setq val (if (= 1 (length collection)) (car collection)
 +
                          (ivy-read (format "Zsh history:") collection))))
 +
      ;; (setq val (replace-regexp-in-string "^:[^;]*;" "" val))
 +
      ;; (setq val (replace-regexp-in-string ".*;" "" val))
 +
      (kill-new val)
 +
      (message "%s => kill-ring" val))))
 +
</source>
 +
 
 +
== Sync environment variables like $PATH from the shell ==
 +
 
 +
On OSx (and elsewhere ?) the $PATH environment variable and `exec-path' used by a windowed Emacs instance will usually be the system-wide default path, rather than that seen in a terminal window. The library [http://melpa.milkbox.net/#/exec-path-from-shell '''exec-path-from-shell'''] (in MELPA) allows the user to set Emacs' <code>exec-path</code> and $PATH from the shell path, so that <code>shell-command</code>, <code>compile</code> and the like work as expected. It also allows other environment variables to be retrieved from the shell, so that Emacs will see the same values you get in a terminal.
 +
 
 +
== Change directory or find files based on "frecency" with z/autojump/fasd ==
 +
 
 +
[https://github.com/clvv/fasd Fasd], z or autojump are popular shell tools for jumping around commonly used directories and (for fasd) to execute actions on files. It uses "[https://github.com/rupa/z/wiki/frecency frecency]" as a metric for determining which directory you intend to jump to based on keyword completions. So if I commonly cd to {{Filename|~/.ssh}}, I can <code>fasd ssh</code> to jump there.
 +
 
 +
The first question is: how to find files with these tools in Emacs ?
 +
 
 +
And next, since they work but can not use the built-in shell completion, how to get them use ido completion in emacs shell mode ?
 +
 
 +
=== Find files with fasd ===
 +
 
 +
There's an emacs package to find files with fasd: https://github.com/steckerhalter/emacs-fasd
 +
If you want it to be more interactive, you can set:
 +
 
 +
<source lang="lisp">
 +
(setq fasd-enable-initial-prompt nil)  ;; don't ask for first query but fire fuzzy completion straight away.
 +
</source>
 +
 
 +
=== Get ido completion to cd in shell ===
 +
 
 +
A great complementary thing with these tools is the use of the shell's completion to change directories, where zsh is a winner. Let's use [[ido]] for that. This feature is available as a minor mode with this code: https://gitlab.com/emacs-stuff/fasd-shell/tree/master
 +
 
 +
Now you can type <code>d my doc TAB</code> and ido will ask what directory (a directory that you already visited with the regular cd and that has both "my" and "doc" in its full path) to cd to.
 +
 
 +
== Use a buffer as $PAGER ==
 +
 
 +
This is possible with [https://github.com/mbriggs/emacs-pager emacs-pager]. Now things like ''less'' will open in a new buffer with colorized output.
 +
 
 +
== Complete git commands and options with pcomplete ==
 +
 
 +
See [https://github.com/leoliu/pcmpl-git-el pcmpl-git] and you'll be able to complete git commands in the emacs shell.
 +
 
 +
== Other settings ==
 +
 
 +
=== Remove duplicates from history ===
 +
 
 +
    (setq history-delete-duplicates t)
 +
 
 +
This will affect all histories, not only shell.
 +
 
 +
= See also =
 +
 
 +
== shell-pop ==
 +
 
 +
'''shell-pop''' to pop up and pop out a shell buffer window easily (installable via ELPA), a bit like guake terminal in Gnome.
 +
 
 +
Here is [https://stackoverflow.com/questions/32977707/sending-bash-commands-to-an-open-terminal-buffer-in-emacs a function] to have one shell per project or perspective.
 +
 
 +
== Shell-here ==
 +
 
 +
Use [https://github.com/ieure/shell-here shell-here] to open an Emacs shell in the current directory.
 +
 
 +
== Terminal here - open an *external* terminal ==
 +
 
 +
[https://github.com/davidshepherd7/terminal-here terminal-here] is meant to open an *external* terminal in the directory associated with the current buffer.
 +
 
 +
== dirswitch ==
 +
 
 +
With [https://github.com/mickeynp/dirswitch.el dirswitch], cycle through directories you've visited, like in fish shell.
 +
 
 +
== elscreen-multi-term ==
 +
 
 +
[https://github.com/wamei/elscreen-multi-term elscreen-multi-term] is to manage a term (not shell) per screen.
 +
 
 +
== sane-term ==
 +
 
 +
[https://github.com/adamrt/sane-term sane-term] is to cycle through terms, not shells.
 +
 
 +
 
 +
[[Category:Shell]]
 +
[[Category:Intermediate]]
 +
[[Category:Programming]]
 +
[[Category:Programming_languages]]

Latest revision as of 14:45, 15 May 2017

Shell-mode gives access to a shell in a normal emacs buffer, meaning you can move around and edit it as usual. A drawback is that you can not launch programs like htop or other ncurses ones, like you would in term-mode (however, there are often built-in alternatives).

Usage

basics

M-x shell

Look at the menu: you have several keys to interact with the shell. Some of them are:

  • [M-p] previous input of command line
  • [M-r] search backward a regexp in commands' history (like C-r in term). Use C-r to cycle.
  • [C-c r] go to beginning of output (useful when you have a large output and want to read through the beginning)
  • [C-c-p] go to beginning of previous output group
  • [C-c-c] send the C-c command to the shell
  • [C-c-o] delete the output of the last command

To launch a shell in the directory of the current buffer, have a look to shell-here: https://github.com/ieure/shell-here (available through ELPA).

re-execute successive commands

Often it is useful to reexecute several successive shell commands that were previously executed in sequence. To do this, first find and reexecute the first command of the sequence. Then type [C-c C-x], that will fetch the following command--the one that follows the command you just repeated. Then type RET to reexecute this command. You can reexecute several successive commands by typing [C-c C-x RET] over and over.

Customisation

Fix, add colors and highlight text

If you have bad colors in the output, try using ansi-mode:

(require 'ansi-color)
(defun colorize-compilation-buffer ()
  (toggle-read-only)
  (ansi-color-apply-on-region (point-min) (point-max))
  (toggle-read-only))
(add-hook 'compilation-filter-hook 'colorize-compilation-buffer)

You can highlight some text based on regexp (useful to see "OK" or warnings):

(add-hook 'shell-mode-hook (lambda () (highlight-regexp "\\[OK\\]" "hi-green-b")))

Make URLs clickable

(add-hook 'shell-mode-hook (lambda () (goto-address-mode )))

Make file paths clickable

Every line representing a path to a file will be colorized and made clickable, so that you can jump to that file and that line, like in compilation-mode (specially useful when compiling a program or running tests):

(add-hook 'shell-mode-hook 'compilation-shell-minor-mode)

Now you can use key bindings from the mode: use [C-x `] (or M-x next-error) (backquote) to go to the next error detected in the shell. You can't do that in an xterm !

Shell completion with a nice menu à la zsh

So you are in shell-mode and you are annoyed that emacs opens a new window with the list of completions when you press TAB and you like the pop-up menu that zsh offers ? The solution is to ask company-mode to do the shell completion. It has a built-in backend company-capf which fetches completion candidates from emacs' completion-at-point-functions (the same mechanism that powers completion in the shell) as such completion offered by company is as accurate as the built-in shell-mode while making the user interface very convenient. We can activate it with:

(add-hook 'shell-mode-hook #'company-mode)
(define-key shell-mode-map (kbd "TAB") #'company-manual-begin))

And the result:

Emacs-shell-company.png

Shell completion with helm

Please see the helm doc.

Change directory with ido-mode

Using ido's completion system for changing directories is a big gain in usability and efficiency for emacs' shell. The little package fasd-shell allows to use the fasd shell utility in conjonction with ido-completion to effectively change directories. Its code comes from this Stack Overflow question: http://stackoverflow.com/questions/20952995/emacs-shell-change-directory-with-ido

Shared and persistent history

By default a shell session inside emacs via shell-mode won't persist accross sessions and won't read your shell's history. The history shell command works as expected, but you can't search (with M-r) a command that you typed in shell-mode in another emacs instance or in a term. The following fixes that.

For comint-mode and derivatives (including shell-mode) the searchable history is read in via comint-read-input-ring, which uses comint-input-ring-file-name which you can set in a mode hook. However I would suggest that you actually set your HISTFILE environment variable to ~/.zsh_history because shell-mode automatically defers to that.

The documentation suggests that:

(add-hook 'shell-mode-hook 'my-shell-mode-hook)
(defun my-shell-mode-hook ()
  (setq comint-input-ring-file-name "~/.zsh_history")  ;; or bash_history
  (comint-read-input-ring t))

Note that if you want to persist more variables (mini-buffer history,…), enable savehist-mode and configure the variables you wish to persist between sessions. See M-x customize-group RET savehist RET.

Search the bash, zsh or fish history with Ivy-mode

Again a code snippet from Redguardto's blog. This snippet defines a function "counsel-yank-bash-history" that one call manually. We are presented an interactive minibuffer (with ivy, à la ido) with all the history. The line we select is put in the kill ring, ready to be pasted.

You can find adaptation for zsh below and here for fish.

(defun counsel-yank-bash-history ()
  "Yank the bash history"
  (interactive)
  (let (hist-cmd collection val)
    (shell-command "history -r") ; reload history
    (setq collection
          (nreverse
           (split-string (with-temp-buffer (insert-file-contents (file-truename "~/.bash_history"))
                                           (buffer-string))
                         "\n"
                         t)))
    (when (and collection (> (length collection) 0)
               (setq val (if (= 1 (length collection)) (car collection)
                           (ivy-read (format "Bash history:") collection))))
        (kill-new val)
        (message "%s => kill-ring" val))))

Here is a slight adaptation for zsh:

(defun counsel-yank-zsh-history ()
  "Yank the zsh history"
  (interactive)
  (let (hist-cmd collection val)
    (shell-command "history -r") ; reload history
    (setq collection
          (nreverse
           (split-string (with-temp-buffer (insert-file-contents (file-truename "~/.zsh_history"))
                                           (buffer-string))
                         "\n"
                         t)))

    (setq collection (mapcar (lambda (it) (replace-regexp-in-string ".*;" "" it)) collection)) ;; for zsh
                      
    (when (and collection (> (length collection) 0)
               (setq val (if (= 1 (length collection)) (car collection)
                           (ivy-read (format "Zsh history:") collection))))
      ;; (setq val (replace-regexp-in-string "^:[^;]*;" "" val))
      ;; (setq val (replace-regexp-in-string ".*;" "" val))
      (kill-new val)
      (message "%s => kill-ring" val))))

Sync environment variables like $PATH from the shell

On OSx (and elsewhere ?) the $PATH environment variable and `exec-path' used by a windowed Emacs instance will usually be the system-wide default path, rather than that seen in a terminal window. The library exec-path-from-shell (in MELPA) allows the user to set Emacs' exec-path and $PATH from the shell path, so that shell-command, compile and the like work as expected. It also allows other environment variables to be retrieved from the shell, so that Emacs will see the same values you get in a terminal.

Change directory or find files based on "frecency" with z/autojump/fasd

Fasd, z or autojump are popular shell tools for jumping around commonly used directories and (for fasd) to execute actions on files. It uses "frecency" as a metric for determining which directory you intend to jump to based on keyword completions. So if I commonly cd to ~/.ssh, I can fasd ssh to jump there.

The first question is: how to find files with these tools in Emacs ?

And next, since they work but can not use the built-in shell completion, how to get them use ido completion in emacs shell mode ?

Find files with fasd

There's an emacs package to find files with fasd: https://github.com/steckerhalter/emacs-fasd If you want it to be more interactive, you can set:

(setq fasd-enable-initial-prompt nil)  ;; don't ask for first query but fire fuzzy completion straight away.

Get ido completion to cd in shell

A great complementary thing with these tools is the use of the shell's completion to change directories, where zsh is a winner. Let's use ido for that. This feature is available as a minor mode with this code: https://gitlab.com/emacs-stuff/fasd-shell/tree/master

Now you can type d my doc TAB and ido will ask what directory (a directory that you already visited with the regular cd and that has both "my" and "doc" in its full path) to cd to.

Use a buffer as $PAGER

This is possible with emacs-pager. Now things like less will open in a new buffer with colorized output.

Complete git commands and options with pcomplete

See pcmpl-git and you'll be able to complete git commands in the emacs shell.

Other settings

Remove duplicates from history

   (setq history-delete-duplicates t)

This will affect all histories, not only shell.

See also

shell-pop

shell-pop to pop up and pop out a shell buffer window easily (installable via ELPA), a bit like guake terminal in Gnome.

Here is a function to have one shell per project or perspective.

Shell-here

Use shell-here to open an Emacs shell in the current directory.

Terminal here - open an *external* terminal

terminal-here is meant to open an *external* terminal in the directory associated with the current buffer.

dirswitch

With dirswitch, cycle through directories you've visited, like in fish shell.

elscreen-multi-term

elscreen-multi-term is to manage a term (not shell) per screen.

sane-term

sane-term is to cycle through terms, not shells.