如何在linux命令行中替换多个文件中的字符串

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

856618 次浏览
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' *

这对我有效:

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

然而,这并没有:sed -i 's/string1/string2/g' *。也许“foo”不是字符串1,“bar”不是字符串2。

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

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

命令故障

grep-r--递归,递归读取每个目录下的所有文件。
grep-l--用火柴打印,打印每个有匹配的文件的名称,而不是打印匹配的行。
grep-i忽略大小写.

xargs:将STDIN转换为参数,遵循此回答
xargs-i@~命令包含@~:用于~命令~中特定位置的参数的占位符,@符号是一个占位符,可以用任何字符串替换。

sed-i:就地编辑文件,没有备份。
Sed/regexp/替换/:将匹配regexp的字符串替换为更换
g全球,替换每个匹配项,而不是只替换第一个匹配项。

类似于Kaspar的答案,但用g标志替换一条线上的所有出现。

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

对于全局不区分大小写:

find ./ -type f -exec sed -i 's/old_string/new_string/gI' {} \;
grep --include={*.php,*.html} -rnl './' -e "old" | xargs -i@ sed -i 's/old/new/g' @

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

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

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

replace "old_string" "new_string" -- *

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

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

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

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

例如。

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

源博客

当使用-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等进一步的参数。


注意:GNUsed不需要文件结尾,sed -i 's/foo/bar/g' *也可以;FreeBSDsed需要扩展名,但允许中间有空格,所以sed -i .bak s/foo/bar/g *可以。

多重编辑命令脚本

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

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

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

@符号表示在以下字符串中应忽略所有特殊字符。

“你也可以使用find-Sed,但我发现这一小行perl工作得很好。

perl -pi -w -e 's/search/replace/g;' *.php
  • -e表示执行以下代码行。
  • -我的意思是就地编辑
  • -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' )

已经列出了一些标准答案。通常,您可以使用找到递归列出文件,然后使用edperl执行操作。

rpl

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

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

rpl -v foo bar '*.txt'

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

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

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

代表

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

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

要使用它,请使用pip install repren查看自述文件为示例。

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

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

例如:

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

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

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

例如

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

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

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

我正在寻找find+sed的组合,因为它在其他情况下对我有用,但不是一次更换几个。

然后我找到了请求ubuntu响应,它帮助我构建了命令行:

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' {} \;

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

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

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

由于一些新功能,它需要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

我举了一个例子来修复python源代码中常见的sheband错误。

您可以尝试grep/ses方法。这是一个适用于GNU ses并且不会破坏git存储库的方法:

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

或者你可以使用爬行动物:)

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

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

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

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

我从另一篇文章中找到了这个(不记得是哪个),虽然不是最优雅的,但它很简单,作为新手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

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

:%s/foo/bar/g
:wn

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

vi*

并按住命令-v,直到它说没有文件剩下。

愚蠢的…黑客和…有效的…

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

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

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

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

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

-i ''将确保您没有备份。

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

使用ack命令会更快,如下所示:

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

此外,如果您在搜索结果中有一个空格。您需要转义它。

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

   # 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分别替换为正确的newstringnewstring1

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

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

#replace_string_files_present_dir

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

replace "oldstring" "newstring" -- *

在文件/bin/replace_string_files_present_dir的末尾。

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

sudo gedit /bin/replace_string_files_present_dir

不要担心您添加的替换字符串的数量,如果找不到oldstring,它们将无效。

假设你想搜索字符串search并在多个文件中用replace替换它,这是我久经考验的单线公式

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

快速grep解释:

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

然后grep输出通过管道传输到用于实际替换文本的ses(通过xargs)。-i标志将直接更改文件。将其删除为一种“空运行”模式。

我只是想添加一个注释来同时做两件事情-找到一个包含字符串的文件,然后使用查找“链接”方法进行替换:

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

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

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

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

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

上面的命令所做的是通过管道传输到grepfind生成包含/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命令借鉴了这个问题的其他答案,并使用+作为分隔符,而不是普通的/,因为/字符正在文件路径中使用。

我使用ag,the_silver_searcher:

ag -0 -l 'old' | xargs -0 sed -ri.bak -e 's/old/new/g';

然后git clean删除. bak文件(rm在git rebaseexec中运行时有错误)

git clean -f '**/*.bak';