【问题标题】:Passing Emacs variables to minibuffer shell commands将 Emacs 变量传递给 minibuffer shell 命令
【发布时间】:2012-04-24 16:52:02
【问题描述】:

我可以通过点击 M-! 快速运行 shell 命令。我想做的一件事是对当前文件执行 shell 快速操作。一个例子是通过 perforce 检出文件:

M-! p4 edit buffer-file-name RET

(是的,有 perforce 集成,但我更感兴趣的是 minishell/变量问题,而不是特定的工作流程)

当然,buffer-file-name 变量在命令发送到 shell 之前不会被计算。

是否有一种简单的即时方式来执行此操作?还是我必须滚动自定义 elisp 函数?

【问题讨论】:

  • perforce 应该与 Emacs 集成,请参阅:emacswiki.org/emacs/PerforceSCM
  • 我将编辑问题以明确说明我的意思只是作为示例。我对如何在将变量传递给 minibuffer shell 之前轻松评估变量感兴趣。

标签: emacs emacs23


【解决方案1】:

目前的 Emacs 似乎有一些内置的东西来达到预期的效果,在M-! (shell-command) 按下<down> 后,你会在提示符上得到你当前正在访问的文件名。现在您可以对其进行编辑以添加要在其上运行的命令。

dired-mode 中,它将为您提供光标当前所在的文件。

【讨论】:

    【解决方案2】:

    每个人似乎都在滚动他们自己的版本,所以这是我的——它会替换当前文件名或标记的目录文件或当前目录文件,只要 % 在 shell 命令中。它遵循与M-! 相同的约定,因此我将其绑定到该约定。

    (defun my-shell-command (command &optional output-buffer error-buffer)
      "Run a shell command with the current file (or marked dired files).
    In the shell command, the file(s) will be substituted wherever a '%' is."
      (interactive (list (read-from-minibuffer "Shell command: "
                                               nil nil nil 'shell-command-history)
                         current-prefix-arg
                         shell-command-default-error-buffer))
      (cond ((buffer-file-name)
             (setq command (replace-regexp-in-string "%" (buffer-file-name) command nil t)))
            ((and (equal major-mode 'dired-mode) (save-excursion (dired-move-to-filename)))
             (setq command (replace-regexp-in-string "%" (mapconcat 'identity (dired-get-marked-files) " ") command nil t))))
      (shell-command command output-buffer error-buffer))
    

    【讨论】:

      【解决方案3】:

      您可以使用C-u M-:eval-expression 带有通用前缀参数)来评估任何 Lisp 表达式并将其值插入当前缓冲区(包括迷你缓冲区,只要您将 enable-recursive-minibuffers 设置为非-nil 值)。

      在您的示例中:C-u M-: buffer-file-name RET

      请注意,表达式的结果是以 Lisp 形式打印的:也就是说,以这样一种方式引用:随后对 read 的调用将构造一个 equal Lisp 值。对于字符串,这意味着用双引号括起来,这可能会被劣质 shell 解释为您所期望的。但是,您可能会遇到包含特殊字符的字符串的问题,这需要 Elisp 和 shell 进行不同的转义。

      更正确的方法是使用shell-quote-argument,就像在菲尔斯的解决方案中一样。这是一个快速定义,它读取 Lisp 表达式并将其值插入到点作为正确引用的 shell 单词:

      (defun eval-to-shell-argument (form)
        (interactive "XEval: ")
        (insert (shell-quote-argument form)))
      

      读取和评估步骤通过使用"X" 作为interactive 的参数自动发生。

      编辑添加:正如@tenpn 所指出的,上述解决方案不适用于在弹出M-! 之类的迷你缓冲区中插入像buffer-file-name 这样的缓冲区局部变量(更准确地说,它插入了小缓冲区的缓冲区本地值,这不太可能有用)。这是一个似乎可行的修订版。如果 minibuffer 处于活动状态,它会在读取和评估表达式时使先前选择的窗口的缓冲区暂时处于活动状态。

      最终编辑:从@Stefan 的回答中,我看到我应该使用(minibuffer-selected-window) 来查找先前选择的窗口。我还添加了(format "%s" ..) 以允许插入非字符串值,同时仍引用字符串中的特殊字符。这是最终版本:

      (defun eval-to-shell-argument ()
        (interactive)
        (let* ((buffer
                (if (minibufferp)
                    (window-buffer (minibuffer-selected-window))
                  (current-buffer)))
               (result
                (with-current-buffer buffer
                  (eval-minibuffer "Eval: "))))
          (insert (shell-quote-argument (format "%s" result)))))
      

      【讨论】:

      • 如果我这样做 M-! p4 edit C-u M-: buffer-file-name RET RET,缓冲区文件名的计算结果为 nil。我猜这是因为它使用的是 minibuffer 的文件名(逻辑上不存在),而不是当前打开的主缓冲区?
      • 而且我不太了解您的功能应该做什么 - 请原谅我的 emacs 新手。能详细点吗?
      • @tenpn: 嗯,你在 minibuffer 中使用 M-: 是绝对正确的——我应该更彻底地测试它。我会看看能不能找到更好的解决方案。
      • @tenpn:我已经发布了一个修改后的解决方案(尽管如果您通常只想使用buffer-file-name,您自己的显然是一种更简单的方法)。如果您想了解更多详细信息,请告诉我。
      【解决方案4】:

      你不能用 M-! 做到这一点,但是你可以从 minibuffer 中计算任意的 elisp,所以写一个函数并不是绝对必要的:

      M-: (shell-command (format "p4 edit %s" (shell-quote-argument buffer-file-name))) RET

      但是,在这种情况下,我认为 eshell 是您想要使用的:

      M-x eshell-command RET p4 edit (eval buffer-file-name) RET

      编辑: 不幸的是,这不起作用,因为在评估时选择了 *eshell cmd* 缓冲区。一种解决方案是:

      M-x eshell-command RET p4 edit (eval buffer-file-name (other-buffer nil t)) RET

      (没那么优雅,抱歉。)

      【讨论】:

      • 您的第一个建议有效,但第二个无效。 (eval buffer-file-name) 似乎没有结果。
      • 诅咒;我没有正确测试。在那个时间点选择了*eshell cmd* 缓冲区。
      【解决方案5】:

      确实使用 C-u M-: 几乎是正确的。我不太确定在eval-to-shell-argument 中使用shell-quote-argument,因为它仅适用于字符串,因此无法使用eval-to-shell-argument 插入数字或符号。您可以尝试以下方法:

      (defun sm-minibuffer-insert-val (exp)
        (interactive
         (list (let ((enable-recursive-minibuffers t))
                 (read-from-minibuffer "Insert: "
                                       nil read-expression-map t
                                       'read-expression-history))))
          (let ((val (with-selected-window (minibuffer-selected-window)
                       (eval exp)))
                (standard-output (current-buffer)))
            (prin1 val)))
      

      然后用(define-key minibuffer-local-map [?\M-:] 'sm-minibuffer-insert-val) 在你的小缓冲区中绑定这个函数。 当然,如果您只想插入缓冲区文件名,那么您的 execute-shell-command-on-buffer 会更简单。

      【讨论】:

      • 你能说得更详细些吗?对不起,我只是不遵循你的功能应该做的事情。我的经验不足,不是你的代码。
      • 啊哈,所以我在编辑的答案中寻找的功能是minibuffer-selected-window!谢谢你的课。
      【解决方案6】:

      确实推出了自己的 elisp 函数,它看起来像这样:

      (defun execute-shell-command-on-buffer (shell-command-text)
          (interactive "MShell command:")
          (shell-command (format shell-command-text (shell-quote-argument buffer-file-name)))
          )
      

      https://gist.github.com/2367513

      我把它绑定到M-",所以现在我的例子可以用:

      M-"p4 edit %sRET

      我不会接受这个作为答案,因为我确实要求不需要函数的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-01-08
        • 1970-01-01
        • 2022-12-13
        • 1970-01-01
        • 1970-01-01
        • 2015-08-05
        • 2021-11-30
        相关资源
        最近更新 更多