如何在 Linux 命令行中替换多个文件中的字符串
- 2024-10-12 09:08:00
- admin 原创
- 81
问题描述:
我需要替换文件夹中大量文件中的字符串,并且只能ssh
访问服务器。我该怎么做?
解决方案 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' *
解决方案 2:
与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' {} ;
忽略.git
目录:
find . -not -path '*/.git/*' -name '*.rb' -exec sed -i 's/old_string/new_string/g' '{}' ;
解决方案 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-case
。
xargs
: 将 STDIN 转换为参数,遵循此答案。
xargs -i@ ~command contains @~
: 要在特定位置使用的参数的占位符~command~
。@
符号是一个可以用任何字符串替换的占位符。
sed -i
:就地编辑文件,无需备份。 :用
sed s/regexp/replacement/
替换匹配的字符串。:全局,对每个匹配进行替换,而不仅仅是第一个匹配。regexp
`replacement`
sed s/regexp/replacement/g
解决方案 4:
这个问题已经列出了几个标准答案。通常,您可以使用find递归列出文件,然后使用sed或perl执行操作。
反向链接
对于大多数快速使用,您可能会发现命令rpl更容易记住。
在所有文件上替换foo
为:bar
`.txt`
rpl -v foo bar '*.txt'
模拟在所有文件中foo.*
递归替换正则表达式:bar
`.txt`
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
请查看 README中的示例。
解决方案 5:
这对我有用:
find ./ -type f -exec sed -i 's/string1/string2/' {} ;
然而,事实并非如此:sed -i 's/string1/string2/g' *
。也许“foo”不应该是string1,“bar”不应该是string2。
解决方案 6:
要替换多个文件中的字符串,您可以使用:
grep -rl string1 somedir/ | xargs sed -i 's/string1/string2/g'
例如
grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'
来源博客
解决方案 7:
假设您想要在多个文件中搜索字符串search
并将其替换为replace
,这是我久经考验的单行公式:
grep -RiIl 'search' | xargs sed -i 's/search/replace/g'
快速 grep 解释:
-R
- 递归搜索-i
- 不区分大小写-I
- 跳过二进制文件(您想要文本,对吗?)-l
- 打印一个简单的列表作为输出。其他命令需要
然后将 grep 输出通过管道传输到 sed(通过 xargs),用于实际替换文本。该-i
标志将直接更改文件。将其删除,以获得一种“试运行”模式。
解决方案 8:
要替换文件中的路径(避免转义字符),您可以使用以下命令:
sed -i 's@old_path@new_path@g'
@ 符号表示应忽略后续字符串中的所有特殊字符。
解决方案 9:
如果字符串中包含正斜杠(/),则可以将分隔符更改为“+”。
find . -type f -exec sed -i 's+http://example.com+https://example.com+g' {} +
该命令将在当前目录中递归运行。
解决方案 10:
如果你有文件列表,你可以使用
replace "old_string" "new_string" -- file_name1 file_name2 file_name3
如果你拥有所有文件,则可以使用
replace "old_string" "new_string" -- *
如果你有带扩展名的文件列表,你可以使用
replace "old_string" "new_string" -- *.extension
解决方案 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:
第一行出现的“foo”将被替换为“bar”。您可以使用第二行来检查。
grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
grep 'foo' -r * | awk -F: {'print $1'} | sort -n | uniq -c
解决方案 13:
在 MacBook Pro 上,我使用了以下命令(受https://stackoverflow.com/a/19457213/6169225启发):
sed -i '' -e 's/<STR_TO_REPLACE>/<REPLACEMENT_STR>/g' *
-i ''
将确保您不进行备份。
-e
用于现代正则表达式。
解决方案 14:
在找到这个问题(和答案)之前,我确实想出了自己的解决方案。我搜索了“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' {} ;
解决方案 15:
真的很差劲,但我无法让任何 sed 命令在 OSX 上正常工作,所以我做了这个愚蠢的事情:
:%s/foo/bar/g
:wn
^- 将这三行复制到我的剪贴板(是的,包括结束的换行符),然后:
六*
并按住 command-v 直到它显示没有剩余文件。
愚蠢...黑客...有效...
解决方案 16:
我只想添加一条注释来同时做两件事 - 找到一个包含字符串的文件,然后使用查找“链接”方法进行替换:
find . -type f -iname *.php -exec fgrep -l "www." {} ; -exec sed -i "s|www||g" {} ;
在这个实际案例中,从 PHP 文件中找到的 URL 中删除不合时宜的“www”。
仅当在文件中找到至少一个匹配项时,才会触发“fgrep -l”,它不会产生其他输出。不要忘记“;”分隔符!
解决方案 17:
grep --include={*.php,*.html} -rnl './' -e "old" | xargs -i@ sed -i 's/old/new/g' @
解决方案 18:
多编辑命令脚本
multiedit [-n PATTERN] OLDSTRING NEWSTRING
根据Kaspar 的回答,我编写了一个 bash 脚本来接受命令行参数,并可选择限制与模式匹配的文件名。将其保存在 $PATH 中并使其可执行,然后只需使用上述命令即可。
脚本如下:
#!/bin/bash
_help="
Replace OLDSTRING with NEWSTRING recursively starting from current directory
multiedit [-n PATTERN] OLDSTRING NEWSTRING
[-n PATTERN] option limits to filenames matching PATTERN
Note: backslash escape special characters
Note: enclose STRINGS with spaces in double quotes
Example to limit the edit to python files:
multiedit -n *.py "OLD STRING" NEWSTRING
"
# 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
解决方案 19:
为了维护我的个人英语节点,我编写了一个实用脚本,帮助递归地替换目录下所有文件的多对旧/新字符串。
多对旧/新字符串在哈希图中进行管理。
可以通过命令行或环境变量设置目录,地图在脚本中是硬编码的,但如果有必要,您可以修改代码以从文件加载。
由于一些新功能,它需要 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):
$1
"
targetDir=$1
elif [[ -v node_dir_en ]]; then # environable set,
echo -e "dir specified (in environment vairable):
$node_dir_en
"
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:
$targetDir
"
exit
fi
# initial map,
declare -A itemMap
itemMap=( ["ɪ"]="i" ["ː"]=":" ["ɜ"]="ə" ["ɒ"]="ɔ" ["ʊ"]="u" ["ɛ"]="e")
# print item maps,
echo 'maps:'
for key in "${!itemMap[@]}"; do
echo -e " $key -> ${itemMap[$key]}"
done
echo -e '
'
# do replace,
for key in "${!itemMap[@]}"; do
grep -rli "$key" * | xargs -i@ sed -i "s/$key/${itemMap[$key]}/g" @
done
echo -e "
Done."
exit
解决方案 20:
流编辑器在使用-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 *
可以工作。
解决方案 21:
使用 ack 命令会快很多,如下所示:
ack '25 Essex' -l | xargs sed -i 's/The fox jump/abc 321/g'
另外,如果搜索结果中有空格,则需要将其转义。
解决方案 22:
如果文件包含反斜杠(通常是路径),您可以尝试如下操作:
sed -i -- 's,<path1>,<path2>,g' *
前任:
sed -i -- 's,/foo/bar,/new/foo/bar,g' *.sh (in all shell scripts available)
解决方案 23:
有一种更简单的方法,即使用一个简单的脚本文件:
# sudo touch /bin/replace_string_files_present_dir
# 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
按下回车键,这将自动用正确的newstring和newstring1分别替换所有包含它们的文件中的oldstring和oldstring1。
它将跳过所有不包含旧字符串的目录和文件。
如果您有多个目录包含需要替换字符串的文件,这可能有助于消除繁琐的输入工作。 您所要做的就是导航到每个目录,然后运行:
#replace_string_files_present_dir
您所要做的就是确保已包含或添加了所有替换字符串,如我上面所示:
replace "oldstring" "newstring" -- *
在文件/bin/replace_string_files_present_dir的末尾。
要添加新的替换字符串,只需在终端中输入以下内容来打开我们创建的脚本:
sudo gedit /bin/replace_string_files_present_dir
不要担心添加的替换字符串的数量,如果找不到旧字符串,它们将不起作用。
解决方案 24:
我从另一篇文章(不记得是哪篇文章)中找到了这篇文章,虽然它不是最优雅的,但它很简单,作为一个 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
解决方案 25:
在命令行上,这取决于您尝试搜索和替换的字符串。 在我的情况下有效的方法如下:
STR="a-?fairly@complicated,*String;but%3A=fortunately&without_quotes.css"
for f in $(grep -lir "$STR" *); do sed -i "s/$STR/newstyle.css/g" $f; done
相对于find
,速度提升了 66%,递归地 grep 网络蜘蛛生成的约 15k 个文件。grep
-with-xargs -i@
答案对我来说不起作用,可能是因为@
符号。
解决方案 26:
我正在给出一个修复 Python 源中常见 shebang 错误的示例。
您可以尝试 grep/sed 方法。以下是与 GNU sed 配合使用且不会破坏 git repo 的方法:
$ grep -rli --exclude '*.git*' '#!/usr/bin/python' . | xargs -I {} \ngsed -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。
解决方案 27:
许多答案都让我感到困惑,因为我需要替换许多文件中的文件路径。虽然其中一个答案提到了这一点,但对我来说不起作用。我的解决方案是:
首先,生成您想要更改的文件名列表。
filelist=($(find /path/to/your/folder | xargs grep '/path/to/fix' | cut -d : -f 1 | tr '
' ' '))
上述命令的作用是,管道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
命令借鉴了该问题的其他答案,并使用+
作为分隔符而不是正常的/
,因为这些/
字符正在文件路径中使用。
解决方案 28:
经过研究,我找到了这个解决方案(如果您只想更改单词,则 b 表示字数限制):
$ STRING_OLD="Peter"; STRING_NEW="John"; find bin/ -type f -name '*.sh' -not -name "*.bak" -print0 | xargs -0 grep -Zl "$STRING_OLD" | xargs -t -0 sed -i.bak 's/'"$STRING_OLD"'/'"$STRING_NEW"'/g'
它适用于带有空格、制表符、换行符和非 ASCII 字符的文件名/目录。
变量 STRING_OLD 是 sed s/regexp/replacement/ 命令的正则表达式。您必须注意这一点。
您还会看到已更改的文件。它只会更改(并备份)在正则表达式中找到 STRING_OLD 的文件。其余文件保持不变(这就是我想要的)。
解决方案 29:
我使用了ag
the_silver_searcher:
ag -0 -l 'old' | xargs -0 sed -ri.bak -e 's/old/new/g';
然后git clean
删除 .bak 文件(rm
在 git rebase 中运行时出现错误exec
)
git clean -f '**/*.bak';
解决方案 30:
以下命令可用于首先搜索文件并替换文件:
find . | xargs grep 'search string' | sed 's/search string/new string/g'
例如
find . | xargs grep abc | sed 's/abc/xyz/g'
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件