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

Posted

技术标签:

【中文标题】通过 Emacs 中的 shell 命令过滤文本【英文标题】:Filtering text through a shell command in Emacs 【发布时间】:2010-09-17 10:25:18 【问题描述】:

在 vi[m] 中,! command which lets me pipe text 通过 shell 命令(如 sort 或 indent)将过滤后的文本返回到缓冲区。在 emacs 中是否有等价物?

【问题讨论】:

有点离题,我认为 Vim 没有 M-|相当于 Emacs,Vim 只能用 shell 输出替换区域;除非你为它编写自定义命令,否则我有点失望。 【参考方案1】:

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

【讨论】:

Jurta 赢得胜利。点赞。我发誓,每当我想我开始了解这个程序时,就会有人过来告诉我我是一个等级初学者。 通常当你需要 Emacs 中的一个特性时,很可能它很久以前就已经实现了。所以首先最好的办法是阅读文档,而不是开始重新发明***;) 谢谢你。不幸的是,我找不到有关此功能的任何文档。 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 中找到它的文档。 这对于使用“perl pie”进行正则表达式搜索和替换非常有用,因为 Emacs Regexp 不能很好地解决它。 C-u M-| perl -pi -e 's/pattern/replace/g' RET。 Evil 也支持 Vim 命令执行此操作 (:%!perl -pi -e ... RET)。【参考方案2】:

这是我几年前写的,它可能对你有帮助:

(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 通过 shell 命令的 "C-k /" 管道。【参考方案3】:

后期编辑:尽管我很感谢大家的支持,但 Jurta 的回答是正确的选择。而且 Greg 的 hack 比我的更简洁。

我会把剩下的留在这里,因为它可能是值得的,但是......


M-x shell-command-on-region,默认绑定到 M-|


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

试试这个(将其加载到*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)
  )

请注意,正如我在 cmets 中所说的,这不是一件很容易做的事情。但我认为它有效。


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

    将“点”(当前光标位置)移动到区域的一端,并使用C-space激活“标记” 将点移动到区域的另一端 大功告成,调用命令

【讨论】:

谢谢,我知道,但它在单独的缓冲区中为我提供了过滤后的文本;它不会取代原来的。 我知道你想要的 vi 行为。还在寻找。 有趣的是,这不仅仅是一个常见问题解答。我一直在 vi(或 vim)中使用这个功能;没有它的编辑器会变得……不那么有趣。 我在vi中也使用这种行为,但不是emacs的方式,可能是定义一个知道你想要什么的模式,让它自动发生。 我将使用 jurta 的建议:)

以上是关于通过 Emacs 中的 shell 命令过滤文本的主要内容,如果未能解决你的问题,请参考以下文章

Shell脚本中常用的文本过滤命令

描述emacs中的绑定过滤

使用 emacs shell 时清除 shell 的命令

在 Emacs 中分析 shell 命令

找不到 Emacs shell 命令

如何在 shell 外使用 sudo 运行 Emacs 命令? [复制]