使用grep和sed [duplicate]递归查找并替换所有文件中的字符串

时间:2022-04-25 19:19:44

This question already has an answer here:

这个问题已经有了答案:

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

5 个解决方案

#1


37  

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

#2


11  

grep -rl $oldstring . | xargs sed -i "s/$oldstring/$newstring/g"

#3


5  

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用于在文件中查找REs并打印匹配行(g/re/p还记得吗?)没有找到文件。有一个非常好的工具,它有一个非常明显的名称来查找文件。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($0,old) { $0 = substr($0,rstart-1) new substr($0,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 /指数()而不是sed和grep你避免需要逃避所有的元字符,可能出现在你的旧或新的字符串+想出一个字符作为你的sed分隔符,不能出现在你的旧或新的字符串,并且你不需要运行grep因为更换只会出现文件,包含你想要的字符串。话虽如此,如果您不想修改文件的时间戳(如果不修改文件的话),那么在执行mv之前,只需对tmp和原始文件执行diff操作,或者在awk之前插入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.

注意:对于包含换行的文件名,上面的方法不起作用。如果你有的话,让我们知道,我们可以告诉你如何处理它们。

#4


3  

sed expression needs to be quoted

需要引用sed表达式

sed -i "s/$oldstring/$newstring/g"

#5


-1  

grep -rl SOSTITUTETHIS . | xargs sed -Ei 's/(.*)SOSTITUTETHIS(.*)/\1WITHTHIS\2/g'

#1


37  

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

#2


11  

grep -rl $oldstring . | xargs sed -i "s/$oldstring/$newstring/g"

#3


5  

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用于在文件中查找REs并打印匹配行(g/re/p还记得吗?)没有找到文件。有一个非常好的工具,它有一个非常明显的名称来查找文件。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($0,old) { $0 = substr($0,rstart-1) new substr($0,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 /指数()而不是sed和grep你避免需要逃避所有的元字符,可能出现在你的旧或新的字符串+想出一个字符作为你的sed分隔符,不能出现在你的旧或新的字符串,并且你不需要运行grep因为更换只会出现文件,包含你想要的字符串。话虽如此,如果您不想修改文件的时间戳(如果不修改文件的话),那么在执行mv之前,只需对tmp和原始文件执行diff操作,或者在awk之前插入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.

注意:对于包含换行的文件名,上面的方法不起作用。如果你有的话,让我们知道,我们可以告诉你如何处理它们。

#4


3  

sed expression needs to be quoted

需要引用sed表达式

sed -i "s/$oldstring/$newstring/g"

#5


-1  

grep -rl SOSTITUTETHIS . | xargs sed -Ei 's/(.*)SOSTITUTETHIS(.*)/\1WITHTHIS\2/g'