【问题标题】:Emacs lisp "shell-command-on-region"Emacs lisp“shell-command-on-region”
【发布时间】:2010-12-05 15:18:13
【问题描述】:

在 GNU Emacs 中,我想在当前选定的文本上运行一个程序 figlet。然后我想评论产生的区域。

我已经知道如何使用标准的 Emacs 命令来做到这一点:

  • 在单词的开头用 C- 设置标记
  • 将光标移到词尾
  • C-u M-x shell-command-on-region RET figlet RET
  • M-x 评论区域 RET

但是,我没有弄清楚如何编写一个 Emacs lisp 程序来完成这一切。这是我的尝试:

(defun figlet-region () 
  (interactive)
  (push-mark)
  (shell-command-on-region "figlet")
  (comment-region (mark) (point))
  (pop-mark)
)

(global-set-key "\C-c\C-f" 'figlet-region)

然后C-<space>; M-x figlet-region产生垃圾:

figlet-region: 参数数量错误: #[(start end command &optional output-buffer replace error-buffer display-error-buffer) "ÆÇÈ \"!É 'jÊ!j;j 0Wb ?Ë`Ì\"Í ÎQÎDRÎÉ!\"& ffÏ )ãÐqÑ!#Ò#p=¬É$]d|e^|Íed ΠÎD¡ÎÉ!\"&â%qÉ$Á&%Ó *Í ÉØ#DÚ#É!\"&*#Ô!#ÕÖ ×!8WrС!qd`Z'o ØcÙÉ\"d'Zb)(Úp!)Û!*" [error-buffer small-temporary-file-directorytemporary-file-directory exit-status error-file replace make-temp -file expand-file-name "scor" nil ...] 9 1945557 (let (string) (unless (mark) (error "The mark is not set now, so there is no region")) (setq string (read -from-minibuffer "区域上的 Shell 命令:" nil nil nil (quote shell-command-history))) (list (region-beginning) (region-end) string current-prefix-arg current-prefix-arg shell-command -default-error-buffer t))], 1

回答

(defun figlet-region (&optional b e) 
  (interactive "r")
  (shell-command-on-region b e "figlet" (current-buffer) t)
  (comment-region (mark) (point)))

(这是基于 Trey Jackson 的回答。)

示例(Lisp 交互模式)

;;  _   _                 _        
;; | |_| |__   __ _ _ __ | | _____ 
;; | __| '_ \ / _` | '_ \| |/ / __|
;; | |_| | | | (_| | | | |   <\__ \
;;  \__|_| |_|\__,_|_| |_|_|\_\___/

示例(CPerl 模式)

#  _   _                 _        
# | |_| |__   __ _ _ __ | | _____ 
# | __| '_ \ / _` | '_ \| |/ / __|
# | |_| | | | (_| | | | |   <\__ \
#  \__|_| |_|\__,_|_| |_|_|\_\___/

【问题讨论】:

    标签: emacs lisp elisp


    【解决方案1】:

    好吧,我不确定垃圾来自哪里,但错误本身来自shell-command-region。在 elisp 中使用时,它至少需要 3 个参数,START ENDCOMMAND

    另外,一般来说,在函数中弄乱标记是不好的做法。以下是 push-mark 的文档对这个主题的看法:

    新手 Emacs Lisp 程序员经常 尝试使用错误的标记 目的。参见文档 `set-mark' 了解更多信息。

    【讨论】:

      【解决方案2】:

      我不确定你想通过推动和弹出标记来完成什么,我相信你会通过这样做获得相同的功能:

      (defun figlet-region (&optional b e) 
        (interactive "r")
        (shell-command-on-region b e "figlet")
        (comment-region b e))
      

      interactive 的参数告诉 Emacs 将区域(点和标记)作为前两个参数传递给命令。

      【讨论】:

      • 请注意,shell-command-on-region 可能会修改该区域,因此最后一行应为 (comment-region (mark) (point)),如我的回答所示。
      • 在我的回答中使用的表单中,它不会修改该区域(除非您从缓冲区 *Shell Command Output* 调用它,这很奇怪)。如果您要使用(mark)(point) - 通过参数传入区域是没有意义的,因为当not 交互调用时它将无法按预期工作。
      • 想象shell-command-on-region 用空字符串替换区域b-e。然后comment-regionDRT?
      • 是的,如果 shell-command-on-region 被调用了更多参数,那么 你说的是正确的。但它是 not (在我的例子中),所以你的观点不适用。我的回答并没有解决他的全部问题,但他已经想通了。
      【解决方案3】:

      在 lisp 程序中使用像 shell-command-on-region 这样的交互式命令不是一个好主意。你应该改用call-process-region

      (defun figlet-region (&optional b e) 
        (interactive "r")
        (call-process-region b e "figlet" t t)
        (comment-region (mark) (point)))
      

      它应该对各种用户选项更具弹性。

      【讨论】:

      • 请注意,在调用 'call-process-region' 中使用 之后的第一个“t”会删除该区域。这个函数的签名是:delete destination display &rest args>