如何grep和替换


251

我需要在目录中的所有文件和子目录中递归搜索指定的字符串,并将该字符串替换为另一个字符串。

我知道找到它的命令可能像这样:

grep 'string_to_find' -r ./*

但是,如何string_to_find用另一个字符串替换每个的实例?


我不相信grep可以做到这一点(我可能是错的)。更简单的方法是使用sed或perl进行替换
Memento Mori

2
尝试使用sed -i 's/.*substring.*/replace/'
Eddy_Em 2013年

2
@Eddy_Em这将用replace替换整行。您需要使用分组来捕获子字符串之前和之后的行的一部分,然后将其放入替换行中。sed -i 's/\(.*\)substring\(.*\)/\1replace\2/'
JStrahl 2014年


@seestackoverflow.com/questions/ 5171901/…
Softwarevamp

Answers:


248

另一种选择是使用查找,然后将其传递给sed。

find /path/to/files -type f -exec sed -i 's/oldstring/new string/g' {} \;

34
在OS X 10.10 Terminal上,-i需要参数的正确扩展字符串。例如,find /path/to/files -type f -exec sed -i "" "s/oldstring/new string/g" {} \;无论如何,提供空字符串仍然会创建备份文件,与手册中所述的不同……
Eonil 2015年

10
为什么得到“ sed:RE错误:非法字节序列”。是的,我-i ""为OS X 添加了。
塔科

2
我在macOS 10.12上遇到了非法字节序列问题,这个问题/答案解决了我的问题:stackoverflow.com/questions/19242275/…
abeboparebop

3
这涉及到每个文件,因此文件时间被修改;并在Windows上将行尾从转换CRLFLF
jww

183

我得到了答案。

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

14
这将扫描匹配的文件两次...一次,grep然后一次sed。使用find方法更有效,但是您提到的此方法确实有效。
cmevoli 2013年

41
在OS X上,你将需要更改sed -i 's/str1/str2/g'sed -i "" 's/str1/str2/g'这个工作。
jdf

6
@cmevoli使用此方法,grep将浏览所有文件,sed仅扫描与匹配的文件grep。使用find其他答案中的方法,find首先列出所有文件,然后sed扫描该目录中的所有文件。所以这种方法并不一定要慢,这取决于有多少场比赛有和之间的搜索速度的差异sedgrepfind
joelostblom

4
OTOH通过这种方式可以让您预览grep在实际替换之前发现的内容,从而大大降低了失败的风险,尤其是对于像我这样的正则表达式n00bs
Lennart Rolland

2
当您的grep替换比sed更聪明时,这也很有用。例如,ripgrep遵循.gitignore,而sed不遵循。
user31389

43

您甚至可以这样做:

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

这将在相对于当前目录的所有文件中搜索字符串“ windows ”,并在每个文件中每次出现的字符串中将“ windows ” 替换为“ linux ”。


2
grep仅当存在不应修改的文件时,此选项才有用。sed在所有文件上运行将更新文件的修改日期,但如果没有匹配项,则使内容保持不变。
三胞胎

@tripleee:小心......但[sed的]离开的内容不变,如果没有匹配”在使用。-i我相信sed每变化倒是文件的文件时,即使内容不变,sed也将转换行结束我不使用sed在Windows中一个Git回购,因为所有CRLF的改变LF
JWW

此命令在-i后需要一个“”,以表示就位替换发生之后(至少在macosx中)将不备份文件。检查手册页以获取详细信息。如果要备份,请在此处放置要创建的文件扩展名。
spinyBabbler

31

这在OS X上最适合我:

grep -r -l 'searchtext' . | sort | uniq | xargs perl -e "s/matchtext/replacetext/" -pi

资料来源:http : //www.praj.com.au/post/23691181208/grep-replace-text-string-in-files


太棒了!也可用于ag:ag "search" -l -r . | sort | uniq | xargs perl -e 's/search/replace' -pi

@sebastiankeller您的Perl命令缺少最后斜杠,这是一个语法错误。
三胞胎

3
为什么是sort -u偶数部分?您希望在什么情况下grep -rl产生两次相同的文件名?
三点三分

5

其他解决方案混合了正则表达式语法。使用Perl / PCRE模式为两种搜索和替换,只有工艺匹配的文件,这个作品非常好:

grep -rlZPi 'match1' | xargs -0r perl -pi -e 's/match2/replace/gi;'

其中match1match2通常相同,但match1可以简化以删除仅与替换有关的更高级的功能,例如捕获组。

翻译:grep递归地列出与该PCRE模式匹配的文件,并用nul分隔以保护文件名中的任何特殊字符,然后通过管道将那些xargs期望以nul分隔的列表的文件名传递到文件名,但是如果没有收到名称,则不会执行任何操作,并perl找到找到匹配项的替代行。

I开关添加到grep以忽略二进制文件。为区分大小写匹配,则删除i从开关grep,并且i连接到替换表达式标志,但不是i上开关perl本身。


Perl本身很具有递归文件结构的能力。实际上,find2perlPerl附带了一个工具,它可以毫无xargs麻烦地完成这种事情。
重奏

@tripleee find不会搜索文件内容,关键是只处理匹配的文件而无需编写Perl程序。
Walf

对于Windows,这是一个不错的解决方案,因为它避免了基于sed的解决方案转换行尾的问题。谢谢!
JamHandy

4

通常不使用grep,而是使用sed -i 's/string_to_find/another_string/g'or perl -i.bak -pe 's/string_to_find/another_string/g'


3

要非常小心使用时findsed在混帐回购协议!如果不排除二进制文件,则可能会出现以下错误:

error: bad index file sha1 signature 
fatal: index file corrupt

为了解决这个错误,你需要恢复的sed更换你new_string与你的old_string。这将还原您替换的字符串,因此您将回到问题的开始。

搜索字符串并替换它的正确方法是跳过find并改用它grep来忽略二进制文件:

sed -ri -e "s/old_string/new_string/g" $(grep -Elr --binary-files=without-match "old_string" "/files_dir")

@hobs的积分


1

这是我会做的:

find /path/to/dir -type f -iname "*filename*" -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

这将查找filename下方文件名中包含的所有文件/path/to/dir,而不是找到的每个文件,搜索带有的行searchstring并替换oldnew

尽管如果要忽略查找文件名中带有filename字符串的特定文件,则可以简单地执行以下操作:

find /path/to/dir -type f -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

上面的操作相同,但是对下找到的所有文件都相同/path/to/dir


0

另一个选择是将perl与globstar一起使用。

启用shopt -s globstar您的.bashrc(或任意位置)允许**全局模式以递归方式匹配所有子目录和文件。

因此,使用perl -pXe 's/SEARCH/REPLACE/g' -i **将递归替换SEARCHREPLACE

-X标志告诉perl“禁用所有警告”-这意味着它不会抱怨目录。

该globstar还允许你做这样的事情sed -i 's/SEARCH/REPLACE/g' **/*.ext,如果你想更换SEARCHREPLACE在扩展所有子文件.ext


“另一种选择是将perl与globstar一起使用...” -不适用于Posixy计算机,例如Solaris。这就是为什么我要特别寻找grepsed
jww
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.