【问题标题】:How to replace a string in multiple files in linux command line如何在linux命令行中替换多个文件中的字符串
【发布时间】:2019-01-16 22:01:33
【问题描述】:

我需要替换文件夹中很多文件中的字符串,只有ssh 可以访问服务器。我该怎么做?

【问题讨论】:

标签: linux string


【解决方案1】:
cd /path/to/your/folder
sed -i 's/foo/bar/g' *

出现的“foo”将被替换为“bar”。

在像 macOS 这样的 BSD 系统上,您需要提供像 -i '.bak' 这样的备份扩展名,否则按照手册页的“风险损坏或部分内容”。

cd /path/to/your/folder
sed -i '.bak' 's/foo/bar/g' *

【讨论】:

  • 如果字符串中有空格或特殊字符,这似乎对我不起作用。知道为什么会这样,还是我需要以某种方式逃避它们?谢谢!
  • 至少对于我的sed 版本,-i 后面需要一个字符串,附加到旧文件名。所以sed -i .bak 's/foo/bar/g' *.xx 将所有.xx 文件移动到等效的.xx.bak 名称,然后使用foo→bar 替换生成.xx 文件。
  • 如果有人想寻找更多选择,在 unix stack exchange 上有一个答案,它涵盖了更多用例站点unix.stackexchange.com/a/112024/13488
  • @MatthewHerbst 转义空格,使用 \ like sed -i 's/foo\ with\ spaces/bar/g' *。我想你已经发现了这么久......但是这样它就会留给其他人发现同样的问题。
  • 对于递归,您可以尝试以下操作。请注意,如果文件列表很大,则它不起作用。 sed -i -e 's/foo/bar/g' $(find /home/user/base/dir)
【解决方案2】:

这对我有用:

find ./ -type f -exec sed -i 's/string1/string2/' {} \;

但是,这没有:sed -i 's/string1/string2/g' *。也许“foo”不应该是 string1,“bar”不是 string2。

【讨论】:

  • 这是因为 sed 对待通配符 * 的方式不同。 [abc]* 表示集合 {a, b, c} 的任意数量的字符。 [a-z0-9]* 的作用类似于通配符 *。
  • 在 OSX 上使用:find ./ -type f -exec sed -i '' -e 's/string1/string2/' {} \;
【解决方案3】:

@kev 的回答很好,但只影响直接目录中的文件。下面的示例使用 grep 递归查找文件。它每次都对我有用。

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

命令分解

grep -r--recursive,递归读取每个目录下的所有文件。
grep -l --print-with-matches,打印每个匹配的文件的名称,而不是打印匹配的行。
grep -i--ignore-案例

xargs:将 STDIN 转换为参数,遵循 answer
xargs -i@ ~command contains @~:参数的占位符在 ~command~ 中的特定位置使用,@ 符号是一个占位符,可以替换为任何字符串。

sed -i:就地编辑文件,没有备份。
sed s/regexp/replacement/:替换字符串匹配regexp替换
sed s/regexp/replacement/g: global,对每个匹配项进行替换,而不是仅对第一个匹配项进行替换。

【讨论】:

  • 这对我不起作用,但确实如此:grep --include={*.php,*.html,*.js} -rnl './' -e "old-word" | xargs -i@ sed -i 's/old-word/new-word/g' @
  • @pymarco 你能解释一下这个命令吗?我不知道为什么你必须在| 之后使用xargs 而不是只使用sed,另外,为什么在xargs 命令中使用-i?我在手册中读到它已被弃用,应该改用-I@ 是否用作模式开始和结束的分隔符?
  • 这个问题是专门针对 linux 的。
  • 在 osx 上没问题:grep -rli 'old-word' * | xargs -I@ sed -i '' 's/2.0.0/latest/g' @
  • 如果您分解一些选项 (rli) 和 @ 符号例如会很好
【解决方案4】:

与 Kaspar 的答案类似,但使用 g 标志替换一行上的所有匹配项。

find ./ -type f -exec sed -i 's/string1/string2/g' {} \;

对于全局不区分大小写:

find ./ -type f -exec sed -i 's/string1/string2/gI' {} \;

【讨论】:

  • 如果你在 OSX 上并且你的模式可能包含点并且你想要就地替换(没有备份文件)你应该使用LC_ALL=C find ./ -type f -exec sed -i '' -e 's/proc.priv/priv/g' {} \;(见this postthis one
  • 这绝对对我有用,因为它可以使用 -name "*.js"' 进行过滤
  • 一个详细的选项会很酷,但是您可以重新 grep 以查看是否进行了更改。注意:对于通配符,尝试 '-name "*.php"' 并且 grep 对递归和通配符不好,你需要添加 --include=*.whatever with -r
  • 不要从签出的 Git 存储库的根目录执行此操作。您可能不小心损坏了.git/ 中的数据库。一定要cd降级。
  • 我刚刚在我的 git repo 中做了这个,现在 git status 返回:error: bad index file sha1 signature.... fatal: index file corrupt。什么给了?
【解决方案5】:
grep --include={*.php,*.html} -rnl './' -e "old" | xargs -i@ sed -i 's/old/new/g' @

【讨论】:

    【解决方案6】:

    如果你有可以使用的文件列表

    replace "old_string" "new_string" -- file_name1 file_name2 file_name3
    

    如果你有所有可以使用的文件

    replace "old_string" "new_string" -- *
    

    如果你有带扩展名的文件列表,你可以使用

    replace "old_string" "new_string" -- *.extension
    

    【讨论】:

    • 实际上,“--file”应该只是“--”,至少在我的版本中
    • 这个实用程序分布在 MySQL 包中。
    • 虽然我想欣赏解决方案,但它可以在不引用正则行作为正则表达式的情况下工作。
    • 这很好用 - 如果你想替换多个文件中的所有字符串,例如结尾".txt" ,你可以做replace "old_string" "new_string" -- *.txt
    • 最好添加从何处获取此replace 实用程序。没有这些信息,这个答案是不完整的。
    【解决方案7】:

    要替换多个文件中的字符串,您可以使用:

    grep -rl string1 somedir/ | xargs sed -i 's/string1/string2/g'
    

    例如

    grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'
    

    Source blog

    【讨论】:

      【解决方案8】:

      当使用-i 开关调用时,流编辑器确实会“就地”修改多个文件,该开关以备份文件结尾作为参数。所以

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

      在此文件夹中的所有文件中将 foo 替换为 bar,但不会下降到子文件夹中。然而,这将为您目录中的每个文件生成一个新的.bak 文件。 要对此目录及其所有子目录中的所有文件递归执行此操作,您需要一个帮助程序(如 find)来遍历目录树。

      find ./ -print0 | xargs -0 sed -i.bak 's/foo/bar/g' *
      

      find 允许您通过指定更多参数(如 find ./ -name '*.php' -or -name '*.html' -print0)来进一步限制要修改的文件。


      注意:GNU sed 不需要文件结尾,sed -i 's/foo/bar/g' * 也可以; FreeBSD sed 需要扩展,但允许在两者之间留一个空格,所以 sed -i .bak s/foo/bar/g * 可以工作。

      【讨论】:

        【解决方案9】:

        多重编辑命令脚本

        multiedit [-n PATTERN] OLDSTRING NEWSTRING
        

        根据 Kaspar 的回答,我制作了一个 bash 脚本来接受命令行参数,并可选择限制与模式匹配的文件名。保存在您的 $PATH 中并使其可执行,然后只需使用上面的命令。

        这是脚本:

        #!/bin/bash
        _help="\n
        Replace OLDSTRING with NEWSTRING recursively starting from current directory\n
        multiedit [-n PATTERN] OLDSTRING NEWSTRING\n
        
        [-n PATTERN] option limits to filenames matching PATTERN\n
        Note: backslash escape special characters\n
        Note: enclose STRINGS with spaces in double quotes\n
        Example to limit the edit to python files:\n
        multiedit -n \*.py \"OLD STRING\" NEWSTRING\n"
        
        # ensure correct number of arguments, otherwise display help...
        if [ $# -lt 2 ] || [ $# -gt 4 ]; then echo -e $_help ; exit ; fi
        if [ $1 == "-n" ]; then  # if -n option is given:
                # replace OLDSTRING with NEWSTRING recursively in files matching PATTERN
                find ./ -type f -name "$2" -exec sed -i "s/$3/$4/g" {} \;
        else
                # replace OLDSTRING with NEWSTRING recursively in all files
                find ./ -type f -exec sed -i "s/$1/$2/" {} \;
        fi
        

        【讨论】:

          【解决方案10】:

          要替换文件中的路径(避免转义字符),您可以使用以下命令:

          sed -i 's@old_path@new_path@g'
          

          @ 符号表示后面的字符串中的所有特殊字符都应该被忽略。

          【讨论】:

          • 正是我在所有其他答案中寻找的。即,如何处理特殊字符,例如更改作为路径的字符串时。谢谢你。在其他答案中似乎是一个很大的疏忽。
          【解决方案11】:

          “你也可以使用 find 和 sed,但我发现 perl 的这一行很好用。

          perl -pi -w -e 's/search/replace/g;' *.php
          
          • -e 表示执行以下代码行。
          • -i 表示就地编辑
          • -w 写警告
          • -p 循环

          "(摘自http://www.liamdelahunty.com/tips/linux_search_and_replace_multiple_files.php

          我最好的结果来自使用 perl 和 grep(以确保文件具有搜索表达式)

          perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )
          

          【讨论】:

            【解决方案12】:

            已经列出了一些标准答案。一般可以用find递归列出文件,然后用sedperl进行操作。

            rpl

            对于大多数快速使用,您可能会发现命令 rpl 更容易记住。

            在所有.txt 文件上将foo 替换为bar

            rpl -v foo bar '*.txt' 
            

            模拟在所有.txt 文件中递归地用bar 替换正则表达式foo.*

            rpl --dry-run 'foo.*' bar '**/*.txt'
            

            您可能需要安装它(apt-get install rpl 或类似名称)。

            代表

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

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

            要使用它,pip install repren。例如Check the README

            【讨论】:

            • 哇,repren 太棒了!只需使用它来更改类名、方法和变量中的部分单词,同时重命名文件以匹配 1,000 多个 C++ 头文件和源文件,并且第一次使用一个命令就可以完美运行。谢谢!
            【解决方案13】:

            如果文件包含反斜杠(通常是路径),您可以尝试以下操作:

            sed -i -- 's,<path1>,<path2>,g' *
            

            例如:

            sed -i -- 's,/foo/bar,/new/foo/bar,g' *.sh (in all shell scripts available)
            

            【讨论】:

              【解决方案14】:

              以下命令可用于先搜索文件并替换文件:

              find . | xargs grep 'search string' | sed 's/search string/new string/g'
              

              例如

              find . | xargs grep abc | sed 's/abc/xyz/g'
              

              【讨论】:

                【解决方案15】:

                在找到这个问题(和答案)之前,我确实制定了自己的解决方案。 我搜索了“replace”、“several”和“xml”的不同组合,因为那是我的应用程序,但没有找到这个特定的组合。

                我的问题:我有包含复杂对象的测试用例数据的 spring xml 文件。对 java 源代码的重构更改了很多类,并且不适用于 xml 数据文件。为了保存测试用例数据,我需要更改分布在多个目录中的所有 xml 文件中的所有类名。同时保存原始 xml 文件的备份副本(尽管这不是必须的,因为版本控制会在这里救我)。

                我一直在寻找 find + sed 的组合,因为它在其他情况下对我有用,但不能同时替换多个。

                然后我找到了ask ubuntu response,它帮助我构建了我的命令行:

                find -name "*.xml" -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;
                

                而且效果很好(嗯,我的案例有六种不同的替代品)。但请注意,它会触及当前目录下的所有 *.xml 文件。因此,如果您对版本控制系统负责,您可能希望首先过滤并仅将那些实际具有您想要的字符串的传递给sed;喜欢:

                find -name "*.xml" -exec grep -e "firstWord" -e "secondWord" -e "thirdWord" {} \; -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;
                

                【讨论】:

                • 对于 windows,我刚刚发现有一种方法可以找到字符串——还没有检查如何替换它——在一个命令中:findstr /spin /c:"quéquieresbuscar" *.xml 这会很方便。
                【解决方案16】:

                为了维护我的个人英文节点,我编写了一个实用程序脚本,帮助递归替换目录下的所有文件的多对旧/新字符串。

                多对旧/新字符串在哈希映射中管理。

                目录可以通过命令行或环境变量设置,地图在脚本中是硬编码的,但如果需要,您可以修改代码以从文件加载。

                由于一些新功能,它需要 bash 4.2。

                en_standardize.sh:

                #! /bin/bash
                # (need bash 4.2+,)
                # 
                # Standardize phonetic symbol of English.
                # 
                # format:
                #   en_standardize.sh [<dir>]
                # 
                # params:
                # * dir
                #   target dir, optional,
                #   if not specified then use environment variable "$node_dir_en",
                #   if both not provided, then will not execute,
                # * 
                # 
                
                paramCount=$#
                
                # figure target dir,
                if [ $paramCount -ge 1 ]; then # dir specified
                    echo -e "dir specified (in command):\n\t$1\n"
                    targetDir=$1
                elif [[ -v node_dir_en ]]; then # environable set,
                    echo -e "dir specified (in environment vairable):\n\t$node_dir_en\n"
                    targetDir=$node_dir_en
                else # environable not set,
                    echo "dir not specified, won't execute"
                    exit
                fi
                
                # check whether dir exists,
                if [ -d $targetDir ]; then
                    cd $targetDir
                else
                    echo -e "invalid dir location:\n\t$targetDir\n"
                    exit
                fi
                
                # initial map,
                declare -A itemMap
                itemMap=( ["ɪ"]="i" ["ː"]=":" ["ɜ"]="ə" ["ɒ"]="ɔ" ["ʊ"]="u" ["ɛ"]="e")
                
                # print item maps,
                echo 'maps:'
                for key in "${!itemMap[@]}"; do
                    echo -e "\t$key\t->\t${itemMap[$key]}"
                done
                echo -e '\n'
                
                # do replace,
                for key in "${!itemMap[@]}"; do
                    grep -rli "$key" * | xargs -i@ sed -i "s/$key/${itemMap[$key]}/g" @
                done
                
                echo -e "\nDone."
                exit
                

                【讨论】:

                  【解决方案17】:

                  我给出了一个修复 python 源代码中常见 shebang 错误的示例。

                  您可以尝试 grep/sed 方法。这是一个适用于 GNU sed 并且不会破坏 git repo 的工具:

                  $ grep -rli --exclude '*.git*' '#!/usr/bin/python' . | xargs -I {} \
                  gsed -i '' -e 's/#!\/usr\/bin\/python/#!\/usr\/bin\/env python/' {}
                  

                  或者你可以使用greptile :)

                  $ greptile -x .py -l -i -g '#!/usr/bin/env python' -r '#!/usr/bin/python' .
                  

                  我刚刚测试了第一个脚本,第二个应该也可以。小心转义字符,我认为在大多数情况下使用 greptile 应该更容易。当然,您可以使用 sed 做很多有趣的事情,为此,最好掌握将它与 xargs 一起使用。

                  【讨论】:

                    【解决方案18】:

                    第一行出现的“foo”将被替换为“bar”。您可以使用第二行进行检查。

                    grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
                    grep 'foo' -r * | awk -F: {'print $1'} | sort -n | uniq -c
                    

                    【讨论】:

                    • 你为什么要链接到你的博客?它包含与您的答案完全相同的文本。
                    • 我喜欢grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
                    【解决方案19】:

                    我从另一篇文章中找到了这个(不记得是哪个),虽然不是最优雅的,但它很简单,作为一个 Linux 新手,我没有遇到任何麻烦

                    for i in *old_str* ; do mv -v "$i" "${i/\old_str/new_str}" ; done
                    

                    如果您有空格或其他特殊字符,请使用 \

                    for i in *old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done
                    

                    子目录中的字符串使用**

                    for i in *\*old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done
                    

                    【讨论】:

                      【解决方案20】:

                      真的很蹩脚,但我无法让任何 sed 命令在 OSX 上正常工作,所以我做了这个愚蠢的事情:

                      :%s/foo/bar/g
                      :wn
                      

                      ^- 将这三行复制到我的剪贴板中(是的,包括结尾的换行符),然后:

                      vi *

                      并按住 command-v 直到它说没有文件了。

                      愚蠢...hacky...有效...

                      【讨论】:

                        【解决方案21】:

                        如果您的字符串中包含正斜杠 (/),您可以将分隔符更改为“+”。

                        find . -type f -exec sed -i 's+http://example.com+https://example.com+g' {} +
                        

                        此命令将在当前目录中递归运行。

                        【讨论】:

                        • 谢谢!帮助将../domain.com 的大量引用更改为domain.com
                        【解决方案22】:

                        在 MacBook Pro 上,我使用了以下内容(灵感来自 https://stackoverflow.com/a/19457213/6169225):

                        sed -i '' -e 's/<STR_TO_REPLACE>/<REPLACEMENT_STR>/g' *
                        

                        -i '' 将确保您不进行任何备份。

                        -e 用于现代正则表达式。

                        【讨论】:

                        • -e 只是告诉 sed 下一个标记是命令。对于扩展正则表达式,请改用-E
                        【解决方案23】:

                        像这样使用 ack 命令会快很多:

                        ack '25 Essex' -l | xargs sed -i 's/The\ fox \jump/abc 321/g'
                        

                        如果您在搜索结果中有空格。你需要逃避它。

                        【讨论】:

                          【解决方案24】:

                          使用简单的脚本文件有一个更简单的方法:

                             # sudo chmod +x /bin/replace_string_files_present_dir
                          

                          在 gedit 或您选择的编辑器中打开文件,我在这里使用 gedit。

                             # sudo gedit /bin/replace_string_files_present_dir
                          

                          然后在编辑器中将以下内容粘贴到文件中

                             #!/bin/bash
                             replace "oldstring" "newstring" -- *
                             replace "oldstring1" "newstring2" -- *
                             #add as many lines of replace as there are your strings to be replaced for 
                             #example here i have two sets of strings to replace which are oldstring and 
                             #oldstring1 so I use two replace lines.
                          

                          保存文件,关闭 gedit,然后退出您的终端或关闭它然后启动它以加载您添加的新脚本。

                          导航到您要编辑多个文件的目录。然后运行:

                            #replace_string_files_present_dir
                          

                          按回车键,这将自动将所有包含它们的文件中的 oldstringoldstring1 替换为正确的 newstring newstring1 分别。

                          它将跳过所有不包含旧字符串的目录和文件。

                          如果您有多个目录包含需要字符串替换的文件,这可能有助于消除繁琐的输入工作。您所要做的就是导航到每个目录,然后运行:

                          #replace_string_files_present_dir

                          您所要做的就是确保您已经包含或添加了所有替换字符串,正如我在上面向您展示的那样:

                          replace "oldstring" "newstring" -- *

                          在文件的末尾/bin/replace_string_files_present_dir

                          要添加新的替换字符串,只需在终端中键入以下内容即可打开我们创建的脚本:

                          sudo gedit /bin/replace_string_files_present_dir

                          不用担心你添加的替换字符串的数量,如果没有找到 oldstring,它们将不起作用。

                          【讨论】:

                          • 通常,当人们在 bash 中询问“如何使用 ${whatever}”时,他们要求的是包含在脚本或 CI 作业 指令中的紧凑版本(比如.gitlab-ci.ymltravis.yml)。每次编写脚本以稍后执行它都是一种反模式,因为您需要编写脚本创建脚本(大多数时候我会变得一团糟)
                          • @zar3bski wut?您确实应该将您的个人 CI 步骤放在单独的 shell 文件中,以便它们可以移植到其他 CI 系统。它还使它们可以在本地进行测试。
                          【解决方案25】:

                          假设您要在多个文件中搜索字符串 search 并将其替换为 replace,则为 my battle-tested, one-line formula

                          grep -RiIl 'search' | xargs sed -i 's/search/replace/g'
                          

                          快速grep解释:

                          • -R - 递归搜索
                          • -i - 不区分大小写
                          • -I - 跳过二进制文件(你想要文本,对吗?)
                          • -l - 打印一个简单的列表作为输出。其他命令需要

                          然后将 grep 输出通过管道传输到 sed(通过 xargs),用于实际替换文本。 -i 标志将直接更改文件。删除它以获得一种“试运行”模式。

                          【讨论】:

                            【解决方案26】:

                            我只想添加一个注释来一次做两件事 - 找到一个包含字符串的文件,然后使用 find 'chaining' 方法进行替换:

                            find  . -type f -iname \*.php -exec fgrep -l "www." {} \; -exec sed -i "s|www||g" {} \;      
                            
                            • 在这个真实案例中,从 PHP 文件中的 url 中删除不合时宜的“www”。

                            • “fgrep -l”只有在文件中找到至少一个匹配项时才会触发,它不会产生其他输出。不要忘记'\;'分隔符!

                            【讨论】:

                              【解决方案27】:

                              我对许多答案的问题是我需要替换许多文件中的文件路径。尽管提供的一个答案提到了这一点,但它对我不起作用。我的解决方案:

                              首先,生成要更改的文件名列表。

                              filelist=($(find /path/to/your/folder | xargs grep '/path/to/fix' | cut -d : -f 1 | tr '\n' ' '))
                              

                              上面的命令所做的是find 管道到grep 生成带有/path/to/fix 内部的文件的名称。但是,grep 也会打印出找到该字符串的行,因此cut 命令会删除它并只保留文件名。 tr 用空格替换换行符,这允许将filelist 存储为数组。

                              for file in "${filelist[@]}"; do sed -i.bak 's+/path/to/fix+/new/path/for/my/file+g' $file; done
                              

                              这个sed 命令借鉴了这个问题的其他答案,并使用+ 作为分隔符而不是普通的/,因为文件路径中使用了/ 字符。

                              【讨论】:

                                猜你喜欢
                                • 2018-06-05
                                • 2018-04-19
                                • 2010-11-17
                                • 1970-01-01
                                • 2013-12-27
                                • 2011-08-14
                                • 2015-08-07
                                • 1970-01-01
                                相关资源
                                最近更新 更多