[英]How to replace a string in multiple files in linux command line
我需要替换一个文件夹中很多文件中的一个字符串,只有ssh
访问服务器。 我怎样才能做到这一点?
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' *
类似于 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' {} \;
@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 包含 @~ :在~command~中的特定位置使用的参数的占位符,@ 符号是一个占位符,可以被任何字符串替换。
sed -i :在原地编辑文件,无需备份。
sed s/regexp/replacement/ :用replacement替换匹配regexp的字符串。
sed s/regexp/replacement/ g : global ,对每个匹配项进行替换,而不是仅对第一个匹配项进行替换。
已经列出了一些标准答案。 通常,您可以使用find递归列出文件,然后使用sed或perl执行操作。
对于大多数快速使用,您可能会发现命令rpl更容易记住。
在所有.txt
文件中将foo
替换为bar
:
rpl -v foo bar '*.txt'
模拟递归地在所有.txt
文件中用bar
替换正则表达式foo.*
:
rpl --dry-run 'foo.*' bar '**/*.txt'
您可能需要安装它( apt-get install rpl
或类似的)。
但是,对于涉及正则表达式和反向替换,或文件重命名以及搜索和替换的更艰巨的工作,我所知道的最通用和最强大的工具是repren ,这是我不久前编写的一个小型 Python 脚本更棘手的重命名和重构任务。 您可能更喜欢它的原因是:
要使用它, pip install repren
。 检查 README以获取示例。
这对我有用:
find ./ -type f -exec sed -i 's/string1/string2/' {} \;
但是,这没有: sed -i 's/string1/string2/g' *
。 也许“foo”不应该是 string1,“bar”不是 string2。
要替换多个文件中的字符串,您可以使用:
grep -rl string1 somedir/ | xargs sed -i 's/string1/string2/g'
例如
grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'
要替换文件中的路径(避免转义字符),您可以使用以下命令:
sed -i 's@old_path@new_path@g'
@ 符号表示应忽略后面字符串中的所有特殊字符。
鉴于您要搜索字符串search
并将其replace
为跨多个文件的替换,这是我经过实战测试的单行公式:
grep -RiIl 'search' | xargs sed -i 's/search/replace/g'
快速grep解释:
-R
- 递归搜索-i
- 不区分大小写-I
- 跳过二进制文件(你想要文本,对吧?)-l
- 打印一个简单的列表作为输出。 需要其他命令然后将 grep 输出通过管道传输到 sed(通过 xargs),用于实际替换文本。 -i
标志将直接更改文件。 删除它以获得一种“试运行”模式。
如果您的字符串中包含正斜杠(/),您可以将分隔符更改为“+”。
find . -type f -exec sed -i 's+http://example.com+https://example.com+g' {} +
此命令将在当前目录中递归运行。
如果您有可以使用的文件列表
replace "old_string" "new_string" -- file_name1 file_name2 file_name3
如果您拥有所有可以使用的文件
replace "old_string" "new_string" -- *
如果您有带有扩展名的文件列表,则可以使用
replace "old_string" "new_string" -- *.extension
第一行出现的“foo”将被替换为“bar”。 您可以使用第二行进行检查。
grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
grep 'foo' -r * | awk -F: {'print $1'} | sort -n | uniq -c
“你也可以使用 find 和 sed,但我发现这一小行 perl 工作得很好。
perl -pi -w -e 's/search/replace/g;' *.php
"(摘自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' )
在 MacBook Pro 上,我使用了以下内容(灵感来自https://stackoverflow.com/a/19457213/6169225 ):
sed -i '' -e 's/<STR_TO_REPLACE>/<REPLACEMENT_STR>/g' *
-i ''
将确保您不进行备份。
-e
用于现代正则表达式。
在找到这个问题(和答案)之前,我确实制定了自己的解决方案。 我搜索了“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' {} \;
真的很蹩脚,但我无法让任何 sed 命令在 OSX 上正常工作,所以我做了这个愚蠢的事情:
:%s/foo/bar/g
:wn
^- 将这三行复制到我的剪贴板中(是的,包括结尾的换行符),然后:
六 *
并按住 command-v 直到它说没有文件了。
愚蠢...hacky...有效...
grep --include={*.php,*.html} -rnl './' -e "old" | xargs -i@ sed -i 's/old/new/g' @
multiedit 命令的脚本
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
我只想添加一个注释来一次做两件事 - 找到一个包含字符串的文件,然后使用 find 'chaining' 方法进行替换:
find . -type f -iname \*.php -exec fgrep -l "www." {} \; -exec sed -i "s|www||g" {} \;
在这个真实案例中,从 PHP 文件中的 url 中删除不合时宜的“www”。
'fgrep -l' 只有在文件中找到至少一个匹配项时才会触发,它不会产生其他输出。 不要忘记'\;' 分隔符!
当使用-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 *
有效。
为了维护我的个人英文节点,我编写了一个实用程序脚本,帮助递归地替换目录下的所有文件的多对旧/新字符串。
多对旧/新字符串在哈希映射中进行管理。
目录可以通过命令行或环境变量设置,地图在脚本中是硬编码的,但如果需要,您可以修改代码以从文件加载。
由于一些新功能,它需要 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
如果文件包含反斜杠(通常是路径),您可以尝试以下操作:
sed -i -- 's,<path1>,<path2>,g' *
前任:
sed -i -- 's,/foo/bar,/new/foo/bar,g' *.sh (in all shell scripts available)
像这样使用 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
按回车键,这将自动将包含它们的所有文件中的oldstring和oldstring1分别替换为正确的newstring和newstring1 。
它将跳过所有不包含旧字符串的目录和文件。
如果您有多个目录包含需要字符串替换的文件,这可能有助于消除繁琐的打字工作。 您所要做的就是导航到每个目录,然后运行:
#replace_string_files_present_dir
您所要做的就是确保您已经包含或添加了所有替换字符串,正如我在上面向您展示的那样:
replace "oldstring" "newstring" -- *
在文件/bin/replace_string_files_present_dir的末尾。
要添加新的替换字符串,只需在终端中键入以下内容打开我们创建的脚本:
sudo gedit /bin/replace_string_files_present_dir
不要担心您添加的替换字符串的数量,如果未找到旧字符串,它们将无效。
以下命令可用于首先搜索文件并替换文件:
find . | xargs grep 'search string' | sed 's/search string/new string/g'
例如
find . | xargs grep abc | sed 's/abc/xyz/g'
我给出了一个修复 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 一起使用。
我从另一篇文章中找到了这个(不记得是哪个),虽然不是最优雅的,但它很简单,作为一个 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
我对许多答案的问题是我需要替换许多文件中的文件路径。 尽管提供的一个答案提到了这一点,但它对我不起作用。 我的解决方案:
首先,生成要更改的文件名列表。
filelist=($(find /path/to/your/folder | xargs grep '/path/to/fix' | cut -d : -f 1 | tr '\n' ' '))
上面的命令所做的是通过管道传输到grep
的find
生成文件的名称,其中包含/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
remove.bak 文件( rm
在 git rebase exec
中运行时出现错误)
git clean -f '**/*.bak';
$ replace“ From”“到”-`find / path / to / folder -type f`
(替换-MySQL数据库系统的字符串替换实用程序)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.