Linux 使用grep和sed递归查找和替换所有文件中的字符串
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15920276/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Find and Replace string in all files recursive using grep and sed
提问by jmazaredo
I'm getting a
我得到一个
sed: -e expression #1, char 22: unterminated `s' command
is there a problem on my script? Also the "oldstring" has special characters
我的脚本有问题吗?“oldstring”也有特殊字符
#!bin/bash
oldstring='<script>"[oldscript]"</script>'
newstring='<script>"[newscript]"</script>'
grep -rl $oldstring /path/to/folder | xargs sed -i s/$oldstring/$newstring/g
采纳答案by wudeng
As @Didier said, you can change your delimiter to something other than /
:
正如@Didier 所说,您可以将分隔符更改为以下内容/
:
grep -rl $oldstring /path/to/folder | xargs sed -i s@$oldstring@$newstring@g
回答by Steven Penny
sed
expression needs to be quoted
sed
表达式需要引用
sed -i "s/$oldstring/$newstring/g"
回答by Ed Morton
The GNU guys REALLY messed up when they introduced recursive file searching to grep. grep is for finding REs in files and printing the matching line (g/re/p remember?) NOT for finding files. There's a perfectly good tool with a very obvious name for FINDing files. Whatever happened to the UNIX mantra of do one thing and do it well?
当 GNU 开发人员将递归文件搜索引入 grep 时,他们真的搞砸了。grep 用于在文件中查找 RE 并打印匹配的行(g/re/p 还记得吗?)而不是用于查找文件。有一个非常好的工具,它有一个非常明显的 FINDing 文件名称。UNIX 的“做一件事并把它做好”的口号发生了什么变化?
Anyway, here's how you'd do what you want using the traditional UNIX approach (untested):
无论如何,以下是使用传统 UNIX 方法(未经测试)执行所需操作的方法:
find /path/to/folder -type f -print |
while IFS= read -r file
do
awk -v old="$oldstring" -v new="$newstring" '
BEGIN{ rlength = length(old) }
rstart = index(grep -rl $oldstring . | xargs sed -i "s/$oldstring/$newstring/g"
,old) { grep -rl SOSTITUTETHIS . | xargs sed -Ei 's/(.*)SOSTITUTETHIS(.*)/WITHTHIS/g'
= substr(##代码##,rstart-1) new substr(##代码##,rstart+rlength) }
{ print }
' "$file" > tmp &&
mv tmp "$file"
done
Not that by using awk/index() instead of sed and grep you avoid the need to escape all of the RE metacharacters that might appear in either your old or your new string plus figure out a character to use as your sed delimiter that can't appear in your old or new strings, and that you don't need to run grep since the replacement will only occur for files that do contain the string you want. Having said all of that, if you don't want the file timestamp to change if you don't modify the file, then just do a diff on tmp and the original file before doing the mv or throw in an fgrep -q before the awk.
并不是通过使用 awk/index() 而不是 sed 和 grep,您就不需要转义可能出现在旧字符串或新字符串中的所有 RE 元字符,并找出一个字符用作 sed 分隔符,它可以' t 出现在您的旧字符串或新字符串中,并且您不需要运行 grep 因为替换只会发生在包含您想要的字符串的文件中。说了这么多,如果您不希望在不修改文件的情况下更改文件时间戳,那么只需在执行 mv 之前对 tmp 和原始文件进行差异处理,或者在执行 mv 之前加入 fgrep -q哦。
Caveat: The above won't work for file names that contain newlines. If you have those then let us know and we can show you how to handle them.
警告:以上不适用于包含换行符的文件名。如果您有这些,请告诉我们,我们可以向您展示如何处理它们。