折腾eshell

doom-emacs lounge看到了eshell,https://www.reddit.com/r/emacs/comments/6y3q4k/yes_eshell_is_my_main_shell/ ambrevar给出了用eshell的理由。这点很重要:

It’s important to understand that shells are not (or should not be) semantically bound to terminal emulator restrictions.

可以用ivy代替fzf,更加方便编辑/搜索的buffer代替| awk | grep

尝试适应这个,但几点比较难受的地方

  • eshell-previous-prompt C-c C-p无效。应该是因为设置了text properties的inhibit-line-move-field-capturefield的原因。我用了这个hack:
(with-eval-after-load 'em-prompt
  (defun eshell-previous-prompt (n)
    "Move to end of Nth previous prompt in the buffer.
See `eshell-prompt-regexp'."
    (interactive "p")
    (beginning-of-line)               ; Don't count prompt on current line.
    ;; PATCH beginning-of-line does not move across the prompt
    (backward-char)
    (eshell-next-prompt (- n))))
  • 不支持input redirection。支持pipe cat file | command,input redirection也应该是能支持的。Emacs-generaljgkamat | maybe I'll stop being lazy and actually put a weekend into it one day
  • 有个想法,cooked mode应用程序可以从minibuffer读入stdin
  • 补全。可以调用fish,比如这个包emacs-fish-completion。 但它是用于pcomplete.el的,补全选项出现在*Completions* buffer中,不好看。可能还得再套一个company-pcomplete.el。有些fish补全命令可能会卡,猜测可能会做些DNS查询之类的事,比如fish -c 'complete -C"rsync -"' 如果有个利用zsh completion,直接提供company的包就好了

另外求一些eshell配置,介绍点workflow等

我暂时抄+写了这些:

(defun +eshell/quit-or-delete-char (arg)
  (interactive "p")
  (if (and (eolp) (looking-back eshell-prompt-regexp nil))
      (eshell-life-is-too-much)
    (delete-char arg)))

;; PATCH no git
(defun +eshell-prompt ()
  (concat (propertize (abbreviate-file-name (eshell/pwd)) 'face 'eshell-prompt)
          (propertize " λ " 'face 'font-lock-constant-face)))

(defun eshell/l (&rest args) (eshell/ls "-l" args))
(defun eshell/e (file) (find-file file))
(defun eshell/md (dir) (eshell/mkdir dir) (eshell/cd dir))
(defun eshell/ft (&optional arg) (treemacs arg))

(defun eshell/up (&optional pattern)
  (let ((p (locate-dominating-file
                (f-parent default-directory)
                (lambda (p)
                  (if pattern
                      (string-match-p pattern (f-base p))
                    t)))
))
    (eshell/pushd p)))

;; PATCH counsel-esh-history
(defun my/ivy-eshell-history ()
  (interactive)
  (require 'em-hist)
  (let* ((start-pos (save-excursion (eshell-bol) (point)))
         (end-pos (point))
         (input (buffer-substring-no-properties start-pos end-pos))
         (command (ivy-read "Command: "
                            (delete-dups
                             (when (> (ring-size eshell-history-ring) 0)
                               (ring-elements eshell-history-ring)))
                            :initial-input input)))
    (setf (buffer-substring start-pos end-pos) command)
    (end-of-line)))

  (defun my/eshell-init-keymap ()
    (evil-define-key 'insert eshell-mode-map
      (kbd "C-d") #'+eshell/quit-or-delete-char
      (kbd "C-r") #'my/ivy-eshell-history
      (kbd "C-p") #'eshell-previous-input
      (kbd "C-n") #'eshell-next-input
      (kbd "<tab>") (lambda () (interactive) (pcomplete-std-complete))
      ))

  (add-hook 'eshell-first-time-mode-hook #'my/eshell-init-keymap)
8 个赞

我的主力Shell 一直是Eshell, 因为我的系统包括Windows/Linux/Mac, 我需要一个跨平台shell

資源:

  1. Master Emacs 的master eshell
  2. Reddit 上面關於的帖子
  3. Youtube 的介紹

https://www.reddit.com/r/emacs/comments/6y3q4k/yes_eshell_is_my_main_shell/

https://www.reddit.com/r/emacs/comments/5qh2sd/using_emacs_27_shell_and_eshell/

插件:

我的配置:

我之前发过关于Eshell 的帖子:

截图:

特性:

  1. 类似fish 的补全
  2. 与Emacs 的完美结合
  3. 跨平台
  4. 可以使用shell 或者lisp 的语法来编写eshell 脚本
  5. 具备主流shell(如bash) 大部分功能

不足:

  1. 不支持输入的重定向
  2. 对top 这些强交互命令支持不好
  3. 缺乏文档

话说,为什么有点链接没有实现缩略图详情的 :zipper_mouth_face:

补充:附加一些我自己日常的Eshell 玩法,之前回帖的时候没有想起来,现在用到又想起来了

  • Eshell 执行着命令,例如ls/cd 等,然后突然想打开某个文件,可以直接在Eshell 使用Emacs 打开目录或者文件,而不需要再在编辑区使用 C-x C-f 打开文件。 eshell

比如我在Eshell窗口 用 cargo new 命令新建了一个Rust 项目了,然后在另外一个打开这个目录/文件。 因为我经常用到dired-other-windowfind-file-other-window, 我就直接用shell 的形式建了alias:

alias fow "find-file-other-window $1"
alias dow "dired-other-window  $1"

意义重大的是:这样就可以将Emacs 的函数当作shell 命令来玩,使Eshell 的shell命令集数量大大增加. @MaskRay

再补充: Eshell 虽说不支持输入重定向,但是Eshell 支持重定向到Emacs, 比如文件,缓冲区,甚至变量:

  • 重定向到 *scratch* 缓冲区
cat mylog.log >> #<buffer *scratch*>
  • 重定向到变量 (这个真的丧心病狂了)

4 个赞

哈哈,真爱生命,远离emacs terminal emulator。

Eshell 偶尔快速用 shell 或者 Windows 的时候用,和 Emacs 自身结合得挺好,但是总有一种性能有问题的感觉,简单用用没啥问题。Linux/Mac 下还是习惯用某个终端模拟器 + zsh。Emacs 自带的终端模拟器都不太行,像 Neovim,VSCode 之类的都自带很好的 terminal 了。现在 emacs-vterm 只处于“勉强可用”的状态,如果作者(或者其他人)能继续开发还是有希望的。

我平常在 macOS 下就是使用 VimR 的终端的,开个 Jupyter-console 使用体验(包含按键绑定)和 Terminal 应用中没区别。

Emacs 中 shell 的主要缺点是性能低下,对强交互命令支持不好。

前几天 讨论 Emacs 26 新增加的特性时 ,我注意到有个更新:eshell 支持 24 位真彩色啦。不过感觉并没有什么用,不太明白 Emacs 上游为啥不把精力投入到 libvte 上,就是死磕 eshell。

libvte 这个相对于 term 有什么优势吗?我看到好多人在说。。。

PS:如果用的是 Linux 并且对 窗口管理器感兴趣的话可以试试 exwm, 可以直接在 emacs 打开其他的终端如:gnome-terminal

根据这份 讨论 24 位真彩支持的 gist 描述:

有好几个新晋的、较受欢迎的终端模拟器,比如 sakura、xfce4-terminal、Terminator、Tilix……还有你提到的 gnome-terminal,都是基于 libvte 做的。哦,还有 neovim 内嵌终端也是基于 libvte 的。

相对 Emacs 的 term 主要优势是速度快吧,而且没有明显毛病。

奇怪,對於單個單詞的命令,如echo<point>ls<point>,在point處回車一次無效,兩次才執行命令,關掉company-mode則修復。回車應該是被company-mode捕獲了

有个问题,我在 eshell 里开了一个 Ruby 的 irb,怎么在 irb 里使用 tab 补全?

因為一个是 shell,一个是 Term,完全两回事

之前讨论过,纠结这个意义不大。

你用过ansi-term吗?

~/.emacs

(defun bash (buffer-name)
  (interactive (list (read-from-minibuffer "Buffer Name:"
                                           nil nil nil nil nil)))
  (ansi-term "/bin/bash" (if (string= "" buffer-name) nil buffer-name)))

然后M-x bash就可以了。我还通过这种方式emacs里面用过vim,还有sudo vim、sudo apt update之类suduoer命令。

参考Running Shells in Emacs.

Emacs 下的各种终端方案都试过了。eshell 和 ansi-term 是我偶尔还会用的。ansi-term 和 Neovim, Atom 和 VSCode 的 terminal 比起来差距很大(个人观感,包括兼容性和速度)。

1 个赞

我最近也迁移到eshell了。

补充一个:

eshell-validate

(defun moon-validate-command ()
  "Validate eshell command."
  (save-excursion
    (beginning-of-line)
    (re-search-forward (format "%s\\([^ ]*\\)" eshell-prompt-regexp)
                       (line-end-position)
                       t)
    (let ((beg (match-beginning 1))
          (end (match-end 1))
          (command (match-string 1)))
      (put-text-property beg end
       'face `(:foreground ,(if (executable-find command)
                                "#98C379"
                              "red"))))))

(add-hook 'eshell-mode-hook (lambda ()
                              "Add `moon-validate-command' to `post-command-hook'."
                              (add-hook 'post-command-hook #'moon-validate-command t t)))

另外counsel有一个counsel-switch-to-shell-buffer,抄来改巴改巴就能用在eshell上:

(defun counsel-switch-to-eshell-buffer ()
    "Switch to a shell buffer, or create one."
    (interactive)
    (ivy-read "Eshell buffer: " (counsel--buffers-with-mode #'eshell-mode)
              :action #'counsel--switch-to-shell
              :caller 'counsel-switch-to-shell-buffer))

配合我之前发帖提到的buffer名字同步pwd:

(defun eshell-sync-dir-buffer-name ()
  "Change eshell buffer name by directory change."
  (when (equal major-mode 'eshell-mode)
    (rename-buffer (format "Esh: %s"
                           (propertize
                            (abbreviate-file-name default-directory)
                            'face `(:foreground ,lunary-pink)))
                   t)))

(add-hook 'eshell-directory-change-hook #'eshell-sync-dir-buffer-name)
(add-hook 'eshell-mode-hook #'eshell-sync-dir-buffer-name)

还有一个fish那种autosuggest好像没人提到:

autosuggest

5 个赞

eshell-skip-prompt 了解一下~

感觉eshell还好,在部署站点时,在新buffer的eshell里ssh上去,然后切换回原来的buffer继续写代码,不需要开新标签

我咋没找到啊……这个函数是哪个文件里的?

em-prompt 里的

早上开机看到了这个:Aweshell

Aweshell 基于 eshell, 希望提供一个开箱即用的 eshell 使用环境, 下载下来就可以使用, 不用浪费时间折腾.

https://github.com/manateelazycat/lazycat-emacs/tree/master/site-lisp/extensions/aweshell