如何在大文件夹层次结构中进行文本替换?

bal*_*lki 11 scripting vim text-processing interactive

我想在一大组文件中搜索和替换一些文本,不包括某些实例。对于每一行,我想要一个提示,询问我是否需要替换该行。类似于 vim 的:%s/from/to/gc(带有cto 提示进行确认),但跨一组文件夹。是否有一些好的命令行工具或脚本可以使用?

Ger*_*ert 19

为什么不使用vim?

在vim中打开所有文件

vim $(find . -type f)
Run Code Online (Sandbox Code Playgroud)

或仅打开相关文件(如 Caleb 建议)

vim $(grep 'from' . -Rl)
Run Code Online (Sandbox Code Playgroud)

然后在所有缓冲区中运行替换

:bufdo %s/from/to/gc | update
Run Code Online (Sandbox Code Playgroud)

您也可以使用sed,但我的 sed 知识有限。


Gil*_*il' 5

您可以使用一个小的 Perl 脚本做一些粗略的事情,该脚本被指示对-l -pe作为参数 ( -i)传递的文件逐行 ( )执行替换:

perl -i -l -pe '
    if (/from/) {                            # is the source text present on this line?
        printf STDERR ("%s: %s [y/N]? ", $ARGV, $_);  # display a prompt
        $r=<STDIN>;                                   # read user response
        if ($r =~ /^[Yy]/) {                          # if user entered Y:
            s/from/to/g;                              # replace all occurences on this line
    }' /path/to/files
Run Code Online (Sandbox Code Playgroud)

可能的改进是为提示的部分着色并支持诸如“替换当前文件中的所有出现”之类的内容。单独提示一行中的每个事件会更难。

第二部分,匹配文件。如果没有涉及太多文件并且您正在运行 zsh,则可以递归匹配当前目录及其子目录中的所有文件:

perl -i -l -pe '…' **/*(.)
Run Code Online (Sandbox Code Playgroud)

如果您的 shell 是 bash ?4,您可以运行perl … **/*,但这会产生虚假的错误消息,因为 sed 将尝试(并失败)在目录上运行。如果您只想在一组文件(例如 C 文件)中执行替换,您可以限制匹配(适用于 bash ?4 或 zsh):

perl -i -l -pe '…' **/*.[hc]
Run Code Online (Sandbox Code Playgroud)

如果您需要更好地控制要替换的文件,或者您的 shell 没有递归目录匹配构造**,或者如果您有太多文件并出现“命令行太长”错误,请使用find. 例如,要在所有命名的文件*.h*.c当前目录及其子目录中执行替换(在较旧的系统上,您可能需要在行尾使用\;代替+(该+形式更快但并非无处不在)。

find . -type f -name '*.[hc]' -exec perl -i -l -pe '…' {} +
Run Code Online (Sandbox Code Playgroud)

话虽如此,如果您需要互动,我会坚持使用交互式编辑器。Gert 已经在 Vim 中展示了一种方法,尽管它需要打开您想要搜索的所有文件,如果有很多文件,这可能是一个问题。

在 Emacs 中,您可以这样做:

  1. 使用M-x find-name-dired(指定顶级目录)或M-x find-dired(指定任意find命令行)收集文件名。
  2. 在所得dired缓冲器,按t标记的所有文件,然后Qdired-do-query-replace-regexp与促使上所标记的文件执行的替代品。