通过Emacs中的shell命令过滤文本


70

在vi [m]中,有一个!命令可以让我通过shell命令(例如sort或indent)将文本传递给管道,然后将过滤后的文本送回缓冲区。emacs中有等效的东西吗?


切线略有偏离,我认为Vim没有M- | 与Emacs等效,Vim只能用shell输出替换该区域;除非您为此编写自定义命令,否则我会有些失望。
暴雪

Answers:


117

您可以选择一个区域并输入`Cu M- | 命令RET',由于shell-command-on-region的交互式前缀参数,它用同一缓冲区中的命令输出替换该区域。


3
于尔塔获胜。赞成。我发誓每次我认为我开始了解这个程序时,都会有人来告诉我我是一名高级初学者。
dmckee ---前主持人小猫,

通常,当您需要Emacs中的功能时,很可能该功能早已实现。因此,首先要做的最好是阅读文档,而不是开始重新发明轮子;)
link0ff

1
谢谢jurta。不幸的是,我找不到此功能的任何文档。
罗希特(Rohit)

2
Rohit,您可以通过打开Emacs手册(C-h i d m Emacs RET') and looking for the command name in the index - i shell-command-on-region RET')打开Emacs的文档
。– link0ff

2
当Emacs Regexp不太适合使用“ perl pie”进行正则表达式搜索和替换时,这非常有用。C-u M-| perl -pi -e 's/pattern/replace/g' RET。Evil也支持Vim命令(:%!perl -pi -e ... RET)。
d11wtq

17

几年前,我写了这篇文章,可能对您有帮助:

(defun generalized-shell-command (command arg)
  "Unifies `shell-command' and `shell-command-on-region'. If no region is
selected, run a shell command just like M-x shell-command (M-!).  If
no region is selected and an argument is a passed, run a shell command
and place its output after the mark as in C-u M-x `shell-command' (C-u
M-!).  If a region is selected pass the text of that region to the
shell and replace the text in that region with the output of the shell
command as in C-u M-x `shell-command-on-region' (C-u M-|). If a region
is selected AND an argument is passed (via C-u) send output to another
buffer instead of replacing the text in region."
  (interactive (list (read-from-minibuffer "Shell command: " nil nil nil 'shell-command-history)
                     current-prefix-arg))
  (let ((p (if mark-active (region-beginning) 0))
        (m (if mark-active (region-end) 0)))
    (if (= p m)
        ;; No active region
        (if (eq arg nil)
            (shell-command command)
          (shell-command command t))
      ;; Active region
      (if (eq arg nil)
          (shell-command-on-region p m command t t)
        (shell-command-on-region p m command)))))

我发现此功能非常有帮助。如果您也觉得它有用,我建议您将它绑定到一些功能键上,以方便我个人使用F3

(global-set-key [f3] 'generalized-shell-command)

优秀的。就像通过外壳命令执行joe的“ Ck /”管道一样。
巴里·凯利

9

后期编辑:就我对投票的赞赏而言,Jurta的答案是正确的方法。格雷格的骇客比我的更整洁。

我将把其余的内容留在这里,因为它可能值钱,但是...


M-x shell-command-on-region,似乎与 M-|默认情况下。


我发现这并不能完全满足Rohit的要求。使用可以C-h f shell-command-on-region显示所需的行为在命令的非交互式版本中可用(通过将参数设置replace为non-nil)。我们应该能够编写一个包装器来做到这一点。

试试这个(将其加载*scratch*并运行M-x eval-buffer,如果可行,请将其复制到您的.emacs文件中):

(defun shell-command-on-region-replace (start end command)
  "Run shell-command-on-region interactivly replacing the region in place"
  (interactive (let (string) 
         (unless (mark)
           (error "The mark is not set now, so there is no region"))
         ;; Do this before calling region-beginning
         ;; and region-end, in case subprocess output
         ;; relocates them while we are in the minibuffer.
         ;; call-interactively recognizes region-beginning and
         ;; region-end specially, leaving them in the history.
         (setq string (read-from-minibuffer "Shell command on region: "
                            nil nil nil
                            'shell-command-history))
         (list (region-beginning) (region-end)
               string)))
  (shell-command-on-region start end command t t)
  )

并请注意,正如我在评论中所说的那样,这并不是一件很令人毛骨悚然的事情。但我认为这可行。


对于任何不知道如何选择区域的读者:

  1. 将“点”(当前光标位置)移动到区域的一端,然后使用 C-space激活“标记”
  2. 将点移到区域的另一端
  3. 完成后,调用命令

谢谢,我知道这一点,但是它给了我过滤后的文本在一个单独的缓冲区中。它不会替代原始文件。
罗希特

我知道您想要的vi行为。还在寻找。
dmckee ---前主持人小猫

有趣的是,这不仅仅是一个常见问题。我一直在vi(或vim)中使用此功能;没有它的编辑器变得……没那么有趣了。
乔纳森·勒夫勒

我也在vi中使用了此行为,但这不是emacs方式,这可能是定义一种模式,该模式知道您想要的内容并使其自动发生。
dmckee ---前主持人小猫,

我将使用jurta的建议:)
Rohit
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.