无痛使用 Emacs 中的交互式 shell

发布于 2025-03-08 22:14:20 字数 4120 浏览 2 评论 0

在 前一篇文章 中,我们讨论了如何创建显式函数来调用单个 shell 命令。现在,让我们进入交互式 shell 中更有趣的内容。

Emacs 交互式 shell 的 API

派生 shell 的主要命令是: shell (可以接一个 buffer 参数) ,与单个 shell 命令类似,我们可以通过操作一些隐藏的变量来更改其行为。重要的变量如包括:

default-directorylocation指定从哪个地方运行 shell
explicit-shell-file-name / shell-file-name执行的 shell 解释器 (例如 bash ...)
explicit--args交互式 shell 的启动参数

注意, shell-command-switch 对对交互式 shell 没有用处,因此没有列在此处。相反,我们有了新的变量 explicit-<INTEPRETER>-args , 它允许为交互式 shell 提供一系列参数。您可能已经在前文中见过它了,函数 eval-with-shell-interpreter 中用到了这个变量。

重用包装器

因此,我们可以重用前面定义的包装器 with-shell-interpreter ,这里有一个小麻烦: 默认的 shell 命令会提示用户指定解释器路径。每次都要提醒太麻烦了,我们可能更系统让它的默认值在启动本地 shell 时使用 shell-file-name 的值,而在启动远程 shell 时使用 default-remote-shell-interpreter 的值。

如果我们想要更改 shell 的位置,那么创建一个显式定义 :interpreter 的命令会更实用。要禁止 shell 提示输入解释器路径,我们必须使用默认的前缀参数来调用它(例如 C-u M-x shell )。要以编程方式重现此行为,我们必须使用 let 将 current-prefix-arg 设置为 '(4)

例如:

(defun my/zsh-local ()
  (interactive)
  (with-shell-interpreter
    :path "~"
    :interpreter "zsh"
    :form
    (let (current-prefix-arg '(4))
      (shell))))

(defun my/bash-on-raspi ()
  (interractive)
  (with-shell-interpreter
    :path "/ssh:pi@raspi:/~"
    :interpreter "bash"
    :form
    (let (current-prefix-arg '(4))
      (shell))))

另一个包装器

这仍然很麻烦。因此,让我们创建另一个派生辅助函数来防止重复。

;; ------------------------------------------------------------------------
;; MAIN

(cl-defun prf-shell (&key path interpreter interpreter-args command-switch)
  "Create a shell at given PATH, using given INTERPRETER binary."
  (interactive)

  (with-shell-interpreter
    :form
    (let* ((path (or path default-directory))
            (is-remote (file-remote-p path))
            (interpreter (or interpreter
                            (if is-remote
                                with-shell-interpreter-default-remote
                              shell-file-name)))
            (interpreter (prf/tramp/path/normalize interpreter))
            (shell-buffer-basename (prf-shell--generate-buffer-name is-remote interpreter path))
            (shell-buffer-name (generate-new-buffer-name shell-buffer-name))
            (current-prefix-arg '(4))
            (comint-process-echoes t))
      (shell shell-buffer-name))
    :path path
    :interpreter interpreter
    :interpreter-args interpreter-args))

;; ------------------------------------------------------------------------
;; HELPERS: BUFFER NAME

(defun prf-shell--generate-buffer-name (is-remote interpreter path)
  (if is-remote
      (prf-shell--generate-buffer-name-remote interpreter path)
    (prf-shell--generate-buffer-name-local interpreter path)))

(defun prf-shell--generate-buffer-name-local (&optional interpreter _path)
  (if interpreter
      (prf-with-interpreter--get-interpreter-name interpreter)
    "shell"))

(defun prf-shell--generate-buffer-name-remote (intepreter path)
  (let ((vec (tramp-dissect-file-name path)))
    (prf-shell--generate-buffer-name-remote-from-vec vec)))

(defun prf-shell--generate-buffer-name-remote-from-vec (vec)
  (let (user host)
    (concat
      (tramp-file-name-user vec) "@" (tramp-file-name-host vec))))

请注意,我们设置 comint-process-echost 来确保能正确地追踪目录变化。目录跟踪(简称 ditrack) 是 Emacs 的一项功能,能在执行 cd 时跟踪当前目录。此外,我们还提供了一些函数来让 shell 缓冲区的名称更加明确。

我们重写的命令变成了:

(defun my/zsh-local ()
  (interractive)
  (prf-shell :path "~" :interpreter "zsh"))

(defun my/bash-on-raspi ()
  (interractive)
  (prf-shell :path "/ssh:pi@raspi:/~" :interpreter "bash"))

prf-shell 的代码可以在包 prf-shell 中找到。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

我很坚强

暂无简介

文章
评论
28 人气
更多

推荐作者

佚名

文章 0 评论 0

今天

文章 0 评论 0

゛时过境迁

文章 0 评论 0

达拉崩吧

文章 0 评论 0

呆萌少年

文章 0 评论 0

孤者何惧

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文