将Emacs变量传递给minibuffer shell命令

将Emacs变量传递给minibuffer shell命令,emacs,emacs23,Emacs,Emacs23,我可以通过点击M-!,快速运行shell命令!。我想做的一件事是对当前文件执行shell快速操作。例如,通过perforce签出文件: M-p4编辑缓冲区文件名RET (是的,有Performce集成,但我更感兴趣的是minishell/变量问题,而不是特定的工作流) 当然,缓冲区文件名变量在命令发送到shell之前不会被计算 有没有一个简单的快速方法可以做到这一点?或者我必须使用自定义elisp函数吗?使用M-!,您不能这样做!,但您可以从minibuffer计算任意elisp,因此编写函数并

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

M-<代码>p4编辑缓冲区文件名RET

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

当然,
缓冲区文件名
变量在命令发送到shell之前不会被计算

有没有一个简单的快速方法可以做到这一点?或者我必须使用自定义elisp函数吗?

使用M-!,您不能这样做!,但您可以从minibuffer计算任意elisp,因此编写函数并不是绝对必要的:

M-:
(shell命令(格式为“p4编辑%s”(shell引用参数缓冲区文件名)))
RET

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

M-x
eshell命令
RET
p4编辑(eval缓冲区文件名)
RET

编辑:不幸的是,这不起作用,因为计算时选择了
*eshell cmd*
缓冲区。一种解决办法是:

M-x
eshell命令
RET
p4编辑(eval缓冲区文件名(其他缓冲区为nil t))
RET


(抱歉,没有那么优雅。)

您可以使用
C-u M-:
eval expression
,带有通用前缀参数)来计算任何Lisp表达式,并在当前缓冲区的某个点插入其值(包括小缓冲区,只要
启用递归小缓冲区
设置为非
nil
值)

在您的示例中:
C-u M-:缓冲区文件名RET

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

更正确的方法是使用
shell quote参数
,就像在phils的解决方案中一样。下面是一个快速的defun,它读取Lisp表达式并将其值作为正确引用的shell单词插入点:

(defun eval-to-shell-argument (form)
  (interactive "XEval: ")
  (insert (shell-quote-argument form)))
通过使用
“X”
作为交互式
的参数,读取和计算步骤将自动进行

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

最终编辑:从@Stefan的回答中,我发现我应该使用
(minibuffer selected window)
来查找以前选择的窗口。我还添加了
(格式“%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)))))
我确实使用了自己的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)))
    )

我将其绑定到M-“,因此现在我的示例可以用以下内容完成:

M-“
p4编辑%s
RET


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

事实上,使用C-u M-:几乎是正确的。我不太确定是否在
eval-to-shell参数中使用
shell-quote参数
,因为它只对字符串起作用,因此无法使用
eval-to-shell参数
插入数字或符号。您可以尝试以下方法:

(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)))
然后使用
将此函数绑定到迷你缓冲区中(定义密钥迷你缓冲区本地映射[?\M-:]'sm迷你缓冲区插入值)

当然,如果您只想插入缓冲区文件名,那么您的
在缓冲区上执行shell命令就更简单了。

每个人似乎都在使用自己的版本,这是我的——它将替换当前文件名或标记的dired文件或当前dired文件,只要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))

M-之后,当前的Emacs似乎内置了一些东西来实现期望的结果
shell命令
)按
,您将在提示符下获得当前访问的文件名。现在,您可以对其进行编辑以添加要在其上运行的命令


dired模式下
它将为您提供光标当前所在的文件。

Performance应与Emacs集成请参见:我将编辑问题,以明确我的意思,这只是一个示例。我感兴趣的是如何在将变量传递到minibuffer shell之前简单地计算它。您的第一个建议有效,但第二个建议无效
(eval buffer file name)
的计算结果似乎为零。诅咒;我没有正确地测试它。
*eshell cmd*
缓冲区在该时间点被选中。如果我选择了
M-!p4编辑C-u M-:缓冲区文件名RET RET
,缓冲区文件名的计算结果为零。我猜这是因为它使用的是minibuffer的文件名(逻辑上不存在),而不是当前打开的主缓冲区?我不太明白你的函数应该做什么-请原谅我的emacs新手。你能说得更详细些吗?@tenpn:Hmm,你在微型缓冲区中使用M-:是绝对正确的——我应该对它进行更彻底的测试。我会看看是否能找到更好的解决方案。@tenpn:我已经发布了一个修改后的解决方案(尽管如果您通常只想和我们合作,您自己的解决方案显然更简单)