使用Emacs递归查找并替换尚未打开的文本文件


212

作为此问题的后续部分,它试图找出如何做这样的事情,这应该很容易,特别是这使我不再习惯于使用Emacs,而是启动了我已经熟悉的编辑器。我在编辑多个文件时经常使用此示例。

在Ultraedit中,我先执行Alt + s,然后显示一个带有以下选项的对话框:查找(包括使用多行正则表达式),替换为,在文件/类型中,目录,区分大小写,仅匹配整个单词,列表更改的文件和搜索子目录。通常,我将首先使用鼠标单击并拖动以选择要替换的文本。

仅使用Emacs本身(在Windows XP上),而不调用任何外部实用程序,如何用bar \ nbaz替换所有foo \ nbar *.c以及*.h某个文件夹及其下面所有文件夹中的文件。也许Emacs并不是执行此操作的最佳工具,但是如何用最少的命令轻松完成呢?

Answers:


370
  1. M-x find-name-dired:将提示您输入根目录和文件名模式。
  2. t以“切换标记”找到的所有文件。
  3. Q“查询替换文件...”:将提示您查询/替换正则表达式。
  4. 继续进行query-replace-regexp::SPACE替换并移至下一个比赛,n跳过比赛,等等。
  5. 按下C-x s以保存缓冲区。(然后y,您可以按n!一次保存所有内容)

10
不,它是递归的。非递归版本在Dired模式下为%m。
克里斯·康威,

5
可能是因为您正在调用C:\ WINDOWS \ system32 \ find.exe,而不是GNU查找。
爵士乐

6
克里斯,斯蒂恩(Steen):可能不完全是您想要的,但是您可以使用'Cx s'(保存一些缓冲区),它会提示您输入每个修改的文件,因此您只需多次单击'y'。
danielpoe

48
C-x s !一次保存所有文件。
cYrus

10
M-,继续搜索和替换(例如,如果磁盘上的文件已更改,则在“还原文件”提示后)。
tfischbach 2012年

37
  • M-x find-name-dired RET
    • 所有文件可能要花费一些时间才能显示在列表中,滚动到底部(M->),直到find finished出现“ ”以确保所有文件都已加载
  • 按下t以“切换标记”找到的所有文件
  • Q“查询替换文件...”:将提示您查询/替换正则表达式。
  • 与query-replace-regexp一样进行:SPACE或y替换并移至下一个匹配项,n跳过匹配项,依此类推。
    • 输入!无需询问即可替换当前文件中的所有匹配项,N并跳过当前文件其余部分的所有可能替换。(N仅适用于emacs 23+)
    • 要对所有文件进行替换而无需进一步询问,请键入Y
  • 调用“ ibuffer”(C-x C-b如果绑定到ibuffer或M-x ibuffer RET)列出所有打开的文件。
  • 键入* u以标记所有未保存的文件,键入S以保存所有标记的文件
  • * * RET取消标记所有标记,或键入D以关闭所有标记的文件

这个答案是从这个答案这个站点和我自己的笔记中组合而成的。使用Emacs 23+。


3
ibufferC-x C-b默认情况下不绑定。
phils

2
好吧,我个人建议人们确实绑定C-x C-bibuffer,因为它比默认绑定好得多。只需添加(global-set-key (kbd "C-x C-b") 'ibuffer)到您的.emacs文件。如果失败,请M-x ibuffer RET在上述说明中使用。
菲利浦斯

好。我想我有emacs启动工具包,这就是为什么我的捆绑软件的原因
Frank Henard

1
文件bla-bla-bla被只读访问,并且停止搜索。如何避免/忽略它并继续更换?谢谢。
费利佩

3
为什么要ibuffer在什么时候可以C-x ssave-some-buffers)和键入!?我不想变得愤怒,只是好奇。PS加描述!NY
d12frosted

17

提供的答案很好,但是我想我会添加一个稍微不同的方法。

这是一个更加互动的方法,并要求wgreprgrepiedit。二者ieditwgrep必须通过MELPA或果酱(使用安装M-x package-list-packages

首先运行M-x rgrep以查找您要查找的字符串。

您将能够指定文件类型/模式和要递归的文件夹。

接下来,您需要从wgrep开始运行C-s C-p

Wgrep将允许您编辑rgrep结果,因此在字符串上设置一个区域以匹配并iedit-modeC-;(取决于你的终端上,你可能需要重新绑定此)

所有事件都将可立即编辑。C-x C-s承诺wgrep。然后C-x s !保存更改的文件。

这种方法的主要好处是可以iedit-mode用来关闭某些匹配项M-;。您也可以使用结果rgrep跳转到文件中,例如,如果您有意外的匹配。

我发现这对于在整个项目中进行源代码编辑和重命名符号(变量,函数名称等)非常有用。

如果您还不知道/使用iedit模式,它是一个非常方便的工具,强烈建议您看一下。


即使不使用iedit,此技术也非常强大。它将知道如何grep(使用lgrep / rgrep变得很容易)变成了知道如何批量搜索替换!
NeilenMarais 2014年

结合rgrep / wgrep / macros很棒。
ocodo 2014年

2
开始的绑定wgrepC-c C-pbtw。
techniao


13

我通常使用其他工具来执行此任务,并且看起来像EmacsWiki的“查找和替换跨文件”条目中提到的许多方法一样,但使用了Findr包看起来非常有前途。

窃取部分源文件

(defun findr-query-replace (from to name dir)
  "Do `query-replace-regexp' of FROM with TO, on each file found by findr.

如何使用findr.el在扩展名为java和hxx的所有文件中找到字符串“ Collectible”?
swdev 2011年

@swdev:Mx查找器-查询-替换RET可收藏的RET <替换> RET。* \。java RET <要开始搜索的目录> RET
ShreevatsaR 2015年

我喜欢这种方法(它避免了直接方法中涉及的所有切换)。我使用了一些方便的功能,这与抛射(项目根发现)相结合,并更换点的东西:gist.github.com/anonymous/055a9d62f9fc824153599724b8f44d57
ATT右击

6

信息来源:1

对于emacs专业版用户:

  1. 调用dired列出目录中的文件,或者如果需要所有子目录,则调用find-dired。
  2. 标记所需的文件。您可以通过键入【%m】来用正则表达式标记。
  3. 键入Q以调用dired-do-query-replace-regexp。
  4. 输入查找正则表达式并替换字符串。〔☛普通elisp regex模式〕
  5. 对于每次出现,键入y替换,输入n跳过。键入【Ctrl + g】可放弃整个操作。
  6. 输入!要在不询问的情况下替换当前文件中的所有匹配项,请N跳过当前文件其余部分的所有可能替换。(仅N是emacs 23)
  7. 要对所有文件进行替换而无需进一步询问,请键入Y。(仅适用于Emacs 23)
  8. 调用ibuffer列出所有打开的文件。键入【* u】标记所有未保存的文件,键入S保存所有标记的文件,键入D关闭所有文件。

Emacs初学者分步指南

选择目标文件

通过在命令行界面提示符中键入“ emacs”来启动emacs。(或者,如果您在“图形用户界面”环境中,则双击Emacs图标)

在目录中选择文件

首先,您需要选择要进行替换的文件。使用图形菜单〖文件▸打开目录〗。Emacs将要求您提供目录路径。键入目录路径,然后按Enter。

现在,将为您显示文件列表,现在您需要标记要使正则表达式查找/替换可用的文件。您可以通过将光标移至所需文件来标记文件,然后按m。按下u取消标记。(要列出子目录,请将光标移到该目录并按i。子目录的内容将在底部列出。)要用正则表达式标记所有文件,请键入【%m】,然后键入您的正则表达式模式。例如,如果要标记所有HTML文件,则键入【%m】,然后键入.html $。(您可以在图形菜单“标记”中找到标记命令的列表(当您处于转向模式时,将显示此菜单)。

在目录及其所有子目录中选择文件

如果要查找/替换目录中的文件(包括数百个子目录),请使用以下方法选择所有这些文件。

致电查找。(您可以通过按【Alt + x)来调用命令),然后键入目录名称⁖/ Users / mary / myfiles

注意:如果在Unix非图形文本终端上使用emacs,并且【Alt + x】不起作用,则等效的击键为【Esc x】。

Emacs将提示您“ Run find(with args):”。如果需要对所有HTML文件进行替换,请键入-name“ * html”。如果您不关心哪种文件,而只关心该目录下的所有文件,则输入“ -type f”。

现在,如上所述标记文件。

交互式查找/替换

现在,您准备好进行交互式查找替换。为了简单起见,假设您只想用“ super”代替“ quick”。现在,调用dired-do-query-replace-regexp。它将提示您输入正则表达式字符串和替换字符串。输入“快速”,输入,然后输入“超级”。

现在,emacs将使用您的模式并检查文件,并在发生匹配时停止并向您显示。发生这种情况时,emacs会提示您,您可以选择进行更改或跳过更改。要进行更改,请键入y。要跳过,请键入n。如果您只是想让emacs继续并对当前文件进行所有此类更改,请键入!。

如果要取消整个操作而不保存所做的任何更改,请键入【Ctrl + g】,然后使用菜单〖文件▸退出Emacs〗退出emacs。

保存更改的文件

现在,在经历了上述考验之后,您还需要执行另一个步骤,那就是保存更改后的文件。

如果使用的是emacs 22版或更高版本,请调用ibuffer进入缓冲区列表模式,然后键入【* u】标记所有未保存的文件,然后键入S来保存所有文件。(就是shift-s)

如果使用的是emacs版本21,则可以执行以下操作:调用list-buffers,然后将光标移至要保存的文件并键入s。它将标记文件以供以后保存操作。输入u取消标记。完成后,键入x以执行所有标记为保存的文件的保存。(在emacs中,打开的文件称为“缓冲区”。忽略那里的其他内容。)

除了上述选项之外,还可以调用save-some-buffers [Ctrl + xs]。然后emacs将显示每个未保存的文件,并询问是否要保存它。

注意:emacs的正则​​表达式与Perl或Python的正则表达式不同,但相似。有关摘要和常见模式,请参阅:Emacs Regex。


2
这个答案应该比我希望投票最多的答案更适合新手。顺便说一句,感谢您激励我把整个内容而不是链接。
Prashant Sharma

答案以source:作为第一行。复制粘贴的理由是:有时到博客的外部链接已过时,然后答案将变得无用。因此,正如维基元建议的那样。我决定复制整个内容。
Prashant Sharma 2014年

5

使用Dired递归深层目录树对于此任务将有些慢。您可以考虑使用tags-query-replace。这确实意味着要花钱来创建标签表,但这通常还是很有用的,而且很快。


刚尝试在包含14,000个文件的目录上使用。花了几秒钟在Emacs中弹出。所以绝对不慢(不再)。
Berend de Boer 2014年

3

对于开放缓冲区,这就是我要做的:

(defun px-query-replace-in-open-buffers (arg1 arg2)
  "query-replace in all open files"
  (interactive "sRegexp:\nsReplace with:")
  (mapcar
   (lambda (x)
     (find-file x)
     (save-excursion
       (goto-char (point-min))
       (query-replace-regexp arg1 arg2)))
   (delq
    nil
    (mapcar
     (lambda (x)
       (buffer-file-name x))
     (buffer-list)))))

3

M-X Dired和t标记所有文件,并Q查询替换其中的所有文本。您可以i在查询替换之前使用命令来扩展子目录。我要添加的关键信息是,如果给i命令加上前缀(control-u),它将提示您输入arg,并且-R参数将递归地将所有内容扩展subdirsdired缓冲区中。因此,现在您可以查询搜索整个目录中的每个文件。


2

另一种选择是使用冰柱搜索。这是另一种增量搜索,它使用您的迷你缓冲区输入的完成来代替搜索命中。修改当前输入时,匹配命中集将在buffer中更新*Completions*

您可以搜索任意数量的文件,缓冲区或加书签的位置,可以使用最小缓冲区模式(例如regexp)匹配进行选择。

当您访问搜索匹配时,您可以根据需要替换整个匹配,也可以只替换与您当前的迷你缓冲区输入匹配的部分。按需更换意味着您不必依次查询每个搜索命中;您可以按任意顺序直接访问所需的匹配。如果您要进行的替换次数有限,则此方法可能比查询替换更有效:跳过详尽的y/n提示。

搜索是在您定义的搜索上下文中进行的-您不仅限于搜索目标文件中的所有文本(例如,您可以跳过注释或特定种类的程序部分)。搜索上下文的一个简单示例是一行,如中所示grep,但是上下文可以是您喜欢的任何模式匹配的文本块。通常,您使用正则表达式定义搜索上下文,但可以改用功能。除了定义自己的语言外,还有针对各种上下文的预定义冰柱搜索命令:文本属性或叠加属性的块,指点事物等。

您还可以排序在不同的排序顺序,方便接入/导航搜索命中。


2

find-name-dired 可以,但是:

  • 您获得的所有文件都匹配相同的单个正则表达式。
  • find-dired在这方面更灵活,但是它也适用于使用一般规则(即使它们可能是任意复杂的)。当然find也有其自己的复杂语言。
  • 如果您只想对find(-name)-dired缓冲区中收集了某些名称的文件进行操作,则需要对其进行标记或删除/忽略不希望对其进行操作的行。

一种替代方法是使用Dired +命令来对(a)标记的文件和(b)递归找到的标记子目录中的所有标记文件(或所有文件,如果未标记的话)起作用。这使您既通用又容易控制文件选择。在Dired模式下,这些“以下”命令都在前缀键上。M-+

例如,M-+ QQ--- query-replace 相同,但是目标文件是在当前目录和任何标记的子目录中标记的所有文件,向下,向下,向下...

是的,使用此前后命令的替代方法是递归插入所有子目录及其子目录,然后使用诸如的顶级命令Q。但是,不打扰插入的子目录通常很方便。

而且,无论如何,您都需要一种快速的方法来递归插入所有此类子目录。同样,Dired +也可以提供帮助。 M-+ M-i递归地插入所有标记的子目录和它们自己的标记的子目录。也就是说,它类似于M-i(在Dired +中插入标记的子目录),但是对子目录进行递归操作。

(所有此类“此处和以下” Dired +命令都在菜单“ 多个” >“ 在此处和下方标记”上。)

您还可以对Emacs 文件集执行Dired操作,该文件集是位于任何位置的文件名的已保存集合。而且,如果您使用冰柱,则可以为文件集中的文件或其他类型的已保存文件列表打开Dired缓冲区。

您还可以为任何Dired缓冲区添加书签,包括使用创建的缓冲区find(-name)-dired。这为您提供了一种快速的方法,以便以后返回到这样的集合(例如,项目集合)。如果您使用Bookmark +,然后为Dired缓冲区添加书签,则它会记录(a)它的ls开关,(b)标记了哪些文件,(c)插入了哪些子目录以及(d)隐藏了哪些(子)目录。当您“跳到”书签时,所有这些都将恢复。 通过Bookmark +,您还可以为整个Dired缓冲区树添加书签---跳转到书签将恢复树中的所有缓冲区。


谁:想要解释一下您为什么拒绝这个答案?
Drew

1

我想提出一个尚未提及的更好的工具,即Helm

它是许多标准Emacs操作(包括完成,搜索等)的理想替代。特别是,它helm-find-files允许在多个选定文件中执行查询替换(包括regexp)。

只需打开helm-find-files,用标记相关文件,M-SPC然后使用F6F7在选定的文件中运行查询替换或查询替换regexp。


可以helm-find-files用regexp代替M-SPC吗?
Nordlöw

-2

它不是Emacs,但是xxdiff带有一个名为xx-rename的工具,该工具可以一次针对多个字符串(例如,从To到FROM到TO)执行此操作,并具有交互式提示,保存所有已修改文件的备份,并生成简短内容上下文所做的更改的日志。这就是我进行大型/全局重命名时倾向于使用的。

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.