【问题标题】:Command line: search and replace in all filenames matched by grep命令行:在 grep 匹配的所有文件名中搜索和替换
【发布时间】:2010-10-03 00:57:21
【问题描述】:

我正在尝试在所有与 grep 匹配的文件中搜索和替换字符串:

grep -n 'foo' * 会给我以下形式的输出:

[filename]:[line number]:[text]

对于 grep 返回的每个文件,我想通过将 foo 替换为 bar 来修改文件。

【问题讨论】:

    标签: linux perl awk sed grep


    【解决方案1】:

    根据您提供的示例,这似乎是您想要的:

    sed -i 's/foo/bar/g' *
    

    它不是递归的(它不会下降到子目录中)。对于在整个树中替换选定文件的一个很好的解决方案,我会使用 find:

    find . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;
    

    *.html 是文件必须匹配的表达式,-i 之后的 .bak 会复制原始文件,扩展名为 .bak(可以是您喜欢的任何扩展名)和 @987654326 @ 在 sed 表达式的末尾告诉 sed 替换一行上的多个副本(而不是仅替换第一个)。要查找的-print 可以方便地显示正在匹配的文件。这一切都取决于您系统上这些工具的确切版本。

    【讨论】:

    • 对 cygwin 用户的警告。 find 和 sed 组合似乎更改了流式传输文件的用户权限。这可以通过使用命令 chmod -R 644 * 从操作 find/sed 时使用的相同目录级别简单地修复。
    • 对不想使用 -i 参数的人的一个警告:如果你不使用它,它就不起作用(不要问我为什么)跨度>
    • @knocte -i 告诉 sed 修改文件,否则它只会将修改后的版本打印到标准输出。如果您不想创建 .bak 文件,只需省略 '.bak' 部分,-i 也可以独立工作。
    • 在 OSX 上,您需要为 find 命令提供一个目录,例如 find . -name '*.html'find directoryname/ -name '*'
    • 我需要添加一个-e,否则它认为's...'部分是后缀sed -ie 's/foo/bar/g' *
    【解决方案2】:

    你的意思是在所有被grep匹配的文件中搜索并替换一个字符串?

    perl -p -i -e 's/oldstring/newstring/g' `grep -ril searchpattern *`
    

    编辑

    因为这似乎是一个相当受欢迎的问题,所以我想更新一下。

    现在我主要使用ack-grep,因为它对用户更友好。所以上面的命令是:

    perl -p -i -e 's/old/new/g' `ack -l searchpattern`
    

    要处理文件名中的空格,您可以运行:

    ack --print0 -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'
    

    您可以使用ack-grep 做更多事情。假设您只想将搜索限制为 HTML 文件:

    ack --print0 --html -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'
    

    如果空白不是问题,它会更短:

    perl -p -i -e 's/old/new/g' `ack -l --html searchpattern`
    perl -p -i -e 's/old/new/g' `ack -f --html` # will match all html files
    

    【讨论】:

    • 我认为这可能与包含空格的文件/文件夹有关。 Can't open Untitled: No such file or directory, <> line 5 在尝试“Untitled Folder/file.txt”时。
    【解决方案3】:

    如果你的sed(1) 有一个-i 选项,那么像这样使用它:

    for i in *; do
      sed -i 's/foo/bar/' $i
    done
    

    如果没有,根据您要使用的语言,以下几种方法会有所不同:

    ruby -i.bak -pe 'sub(%r{foo}, 'bar')' *
    perl -pi.bak -e 's/foo/bar/' *
    

    【讨论】:

    • for i in *; do ... 是多余的,sed 可以将文件列表作为参数。
    • @Jens 为什么不通过将您自己的示例添加到上述示例来改进此答案?
    • 变量应该总是被引用for i in *; do; sed -i 's/foo/bar/g' "$i"; done
    【解决方案4】:

    我喜欢并使用了上述解决方案或在数千个文件中进行系统范围的搜索和替换:

    find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' {} \;
    

    我假设使用 '*.htm?'它搜索并查找 .htm 和 .html 文件,而不是 .html。

    我将 .bak 替换为系统更广泛使用的波浪号 (~),以便更轻松地清理备份文件。

    【讨论】:

      【解决方案5】:

      这可以使用 grep 而无需使用 perl 或 find。

      grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @
      

      【讨论】:

      • xargs 在 OSX 或 BSD 上没有 -i openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man1/… 你的意思是使用大写的“I”吗?
      • 我不知道-i 不适用于其他操作系统。在 ubuntu 上为我工作。
      • 我的 xargs 手册页(在 Ubuntu 上)说 -i 已被弃用,而是使用 -I。所以,我们应该说:grep -rli 'old-word' * | xargs -I filepath sed -i 's/old-word/new-word/g' filepath
      【解决方案6】:

      find . -type f -print0 | xargs -0 <sed/perl/ruby cmd> 将一次处理多个包含空格的文件名,每批加载一个解释器。更快。

      【讨论】:

      • @knocte,“cmd”是一个标记,用于选择任何给定工具的整个搜索和替换命令。此答案回答了有关如何处理包含空格的文件名的问题。
      【解决方案7】:

      已经给出了使用 findsed

      的答案

      find -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

      可能是标准答案。或者您可以使用perl -pi -e s/foo/bar/g' 代替sed 命令。

      对于大多数快速使用,您可能会发现命令 rpl 更容易记住。这是替换(foo -> bar),递归地在当前目录中的所有文件上:

      rpl -R foo bar .

      它在大多数 Linux 发行版上默认不可用,但可以快速安装(apt-get install rpl 或类似的)。

      但是,对于涉及正则表达式和反向替换、文件重命名以及搜索和替换的更棘手的工作,我所知道的最通用和最强大的工具是 repren,一个我为一些更棘手的重命名和重构任务编写了一个小的 Python 脚本。您可能更喜欢它的原因是:

      • 支持文件重命名以及文件内容的搜索和替换(包括在目录之间移动文件和创建新的父目录)。
      • 在您承诺执行搜索和替换之前查看更改。
      • 支持具有反向替换、整个单词、不区分大小写和保留大小写(替换 foo -> bar、Foo -> Bar、FOO -> BAR)模式的正则表达式。
      • 适用于多个替换,包括交换(foo -> bar 和 bar -> foo)或非唯一替换集(foo -> bar,f -> x)。

      Check the README 示例。

      【讨论】:

        【解决方案8】:

        这实际上比看起来容易。

        grep -Rl 'foo' ./ | xargs -n 1 -I % sh -c "ls %; sed -i 's/foo/bar/g' %";
        
        • grep 递归遍历你的树 (-R) 并仅打印文件名 (-l),从当前目录 (./) 开始
        • 通过管道传送到 xargs,它一次处理一个 (-n 1),并在 shell 命令 (sh -c) 中使用 % 作为占位符 (-I %)
        • 在shell命令中,首先打印文件名(ls %;)
        • 然后 sed 执行内联操作 (-i),用 bar (foo/bar) 替换 foo ('s/'),全局 (/g) 对文件(同样,用 % 表示)

        简单易懂。如果你很好地掌握了 find、grep、xargs、sed 和 awk,那么在 bash 中操作文本文件几乎没有什么是不可能的:)

        【讨论】:

        • 呸,忽略,这个 pymarco 已经在上面介绍过了。留下我的答案来解释。
        【解决方案9】:

        2022 年答案。
        https://github.com/BurntSushi/ripgrep/blob/master/FAQ.md#how-can-i-search-and-replace-with-ripgrep

        rg foo --files-with-matches | xargs sed -i 's/foo/bar/g'
        

        将在 ripgrep 找到 foo 模式的文件中用 'bar' 替换所有 'foo' 实例。 sed 的 -i 标志表明您正在就地编辑文件,并且 s/foo/bar/g 表示您正在执行模式 foo 对 bar 的替换,并且您正在全局执行此替换(所有出现的每个文件中的模式)。

        【讨论】:

          猜你喜欢
          • 2011-07-29
          • 1970-01-01
          • 2013-10-14
          • 2018-10-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多