在Emacs中,如何仅在匹配的行上替换

时间:2011-11-30 23:02:09

标签: emacs elisp

例如:在VIM中,

test
sample replace
test1
replace
sample test2

:g / sample / s / replace / test3 / - 只会在匹配的“sample”行中替换“replace”

test
sample test3
test1
replace
sample test2

在VIM中,只能在非匹配线上进行替换 :v / sample / s / replace / test3 - 仅在不包含“sample”的非匹配行上替换“replace”

test
sample replace
test1
test3
sample test2

如何使用emacs完成上述事情?

6 个答案:

答案 0 :(得分:17)

您使用的是Emacs 24吗?如果是这样,您可以使用内置的发生编辑模式:

  • 首先使用其中一个出现的函数来获得与您的模式匹配的行的缓冲区:
    M-x occur RET (模式) RET
  • 现在按 e 进入发生编辑模式,仅对这些行进行交互式编辑。
  • 搜索并替换。
  • 最后, C-c C-c 退出出现编辑模式, q 取消发生的缓冲区。

有些第三方库可以在早期版本的Emacs上执行类似的技巧。我相信moccur-edit就是其中之一,但我从未使用它。

编辑:关于问题的第二部分,虽然我不熟悉更直接的实现方法,但我们可以通过这种方法来实现它:

  • Mx occur RET . RET 获取包含 all 的缓冲区线
  • C-x C-q 使缓冲区可写
  • M-x flush-lines RET (pattern) RET 删除匹配行
  • C-x C-q 使缓冲区只读再次发送
  • e 调用发生编辑模式(然后像以前一样继续......)

答案 1 :(得分:1)

我认为没有内置任何内容,但有些elisp可以做到:

(defun my-replace-on-matching-lines (&optional arg)
  "Replace text on lines that match a regexp.
With prefix arg, replace on non-matching lines."
  (interactive "P")
  (let* ((regexp (concat ".*"
                         (read-from-minibuffer
                          (concat "Replace on lines "
                                  (if arg "not " "")
                                  "matching regexp: "))))
         (replace (read-from-minibuffer "Replace: "))
         (with (read-from-minibuffer (concat "Replace " replace " with: ")))
         match)
    (save-excursion
      (goto-char (point-min))
      (while (not (eobp))
        (setq match (looking-at regexp))
        (when (if arg (not match) match)
          (while (search-forward replace (point-at-eol) t)
            (replace-match with nil t)))
        (forward-line)))))

答案 2 :(得分:1)

使用Icicles searchreplace

Icicles 搜索背后的主要思想是首先定义搜索 上下文 ,然后在这些上下文中搜索子串和正则表达式等模式。您可以轻松地搜索non-contexts(上下文之外的文本)作为上下文,从而解决您的第二个问题。

在这种情况下,搜索上下文是包含sample的行。此正则表达式定义了上下文:.*sample.*,因为.匹配除换行符之外的任何字符。

对于此搜索,您不希望替换整个搜索上下文,因此您应将选项icicle-search-replace-whole-candidate-flag设置为nil。默认情况下它是非零的 - 您只需要按 M -_ (一次)来切换它。这使得当前迷你缓冲区输入与被替换的文本匹配,而不是使整个上下文成为替换文本。

您可以按任意顺序访问所需的任何上下文(包含sample)的任何内容。如果你愿意,你可以在其中一些或全部循环。您甚至可以通过各种方式对上下文进行排序,以便于比较或更改循环顺序。 (排序不会以任何方式更改文本 - 上下文按缓冲区*Completions*排序。)

但是在这里你真的对那些包含replace的上下文感兴趣,所以你在迷你缓冲区中输入replace。只有这些背景仍然是候选人。如果您更改了迷你缓冲区输入,则搜索命中集 - 匹配的上下文 - 会动态更改。同样,您可以按任何顺序访问任何匹配的上下文,在它们之间循环等等。

以下是步骤:

  1. C -` ,启动冰柱搜索。
  2. 系统会提示您输入定义上下文的正则表达式。键入.*sample.*,然后按 RET
  3. 您点击 S-TAB 以查看文件中突出显示的内容并列在缓冲区*Completions*中。搜索上下文是完成候选
  4. 您键入replace以将上下文缩小为包含replace的内容。
  5. 你点击 M -_ (元下划线)来切换整个上下文替换OFF(假设它当前是ON,默认情况下是这样)。
  6. 您点击 C-down (控制向下箭头)移至第一个匹配的上下文。
  7. 您点击 S-RET 来替换当前输入(replace)匹配的上下文部分。
  8. 由于您尚未定义替换件,系统会提示您输入替换件。您输入test3
  9. 替换replace的第一次出现。你再次点击 S-RET 替换下一个,然后再替换下一个,等等。
  10. 请注意,这可让您在同一个上下文中逐个替换多次出现的replace

    如果您知道要替换{em>所有出现的replace,请在步骤9中点击 M- | 来执行此操作。

    替换只是按需。你基本上只是在寻找。您可以决定访问哪些搜索。在我给出的示例中,您使用C-down在搜索匹配中循环,但您可以轻松访问某些匹配。

    当您访问搜索时,您决定是否在那里进行替换。您不会依次查询每次点击并被迫回复yn等,如query-replace中所示。

    (如果过滤后只有一个搜索上下文,例如,只有一行同时包含samplereplace,那么您将立即转到该上下文,而不是有机会替换部分内容。)

    要搜索 -contexts而不是上下文---即包含sample的行之外的文本,请点击 CM-~ 在搜索完成期间,在内部/外部环境中切换搜索。 (切换从下一次搜索开始生效,而不是你点击 C-M-〜的那个。)

    (对于包含grep的行,另一种方法是sample,但存在一些差异, 然后点击*grep*缓冲区中的 C -` ,然后按上述步骤进行操作--- IOW,使用grep定义搜索上下文。)

答案 3 :(得分:0)

您可以使用regluar表达式搜索/替换:

M-x query-replace-regexp   (or C-M-%)
\(sample.*\)replace      RET
\1test3

这会在replace之前test3的所有行上替换sample的{​​{1}}。

这并不是您所要求的(replace可以在线上的任何位置)。

答案 4 :(得分:0)

  • M-x replace-regexp(或query-replace-regexp
  • 要匹配的模式:replace\(.*sample.*\)\|\(sample.*?\)replace\(.*\)
  • 替换文字:\2test3\1\3

这种方法只处理问题的第一部分。第二部分可能需要编写一些Lisp代码,因为Emacs regexp不包含先行断言。

答案 5 :(得分:-1)

到目前为止,这个问题最明显的答案是不存在的......

恶模式

如果您使用evil-mode,则可以在Emacs中使用相同的:g:global)和:v:vglobal)ex命令。

:g/sample/s/replace/test3
:v/sample/s/replace/test3

警告: 以斜杠结束命令,否则会破坏这些命令的行为。斜杠后跟一个或多个标志就可以了。