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

jma*_*edo 31 linux bash recursion grep sed

我得到了一个

sed: -e expression #1, char 22: unterminated `s' command 
Run Code Online (Sandbox Code Playgroud)

我的剧本有问题吗?"oldstring"也有特殊字符

#!bin/bash
oldstring='<script>"[oldscript]"</script>'
newstring='<script>"[newscript]"</script>'
grep -rl $oldstring /path/to/folder | xargs sed -i s/$oldstring/$newstring/g
Run Code Online (Sandbox Code Playgroud)

wud*_*eng 42

正如@Didier所说,您可以将分隔符更改为以下内容/:

grep -rl $oldstring /path/to/folder | xargs sed -i s@$oldstring@$newstring@g
Run Code Online (Sandbox Code Playgroud)

  • 考虑到oldstring或newstring的许多可能内容,上面的内容会以很多有趣的方式失败. (6认同)
  • 在任一字符串变量中使用任何shell globbing字符或@来尝试上述操作.globbing chars可以/将匹配你的目录中的文件名,如果不是今天,那么将来某一天,当你最后期望它时,@将终止你的sed命令.如果oldstring在不同位置包含空格,它也会失败,更不用说两个字符串是否包含换行符.OP特别说'the oldstring'有特殊的字符`所以这是一个很大的提示,我刚刚描述的任何内容都可能发生. (3认同)
  • 另外请注意,在某些环境中,特别是Darwin/MacOSX,您需要提供明确的备份扩展名,例如'sed -i .bak'. (3认同)

Sre*_* GS 13

grep -rl $oldstring . | xargs sed -i "s/$oldstring/$newstring/g"
Run Code Online (Sandbox Code Playgroud)


Ed *_*ton 5

当他们将递归文件搜索引入grep时,GNU家伙真的搞砸了.grep用于在文件中查找RE并打印匹配行(g/re/p记得?)不用于查找文件.有一个非常好的工具,有一个非常明显的FINDing文件名称.无论发生什么事情发生在做一件事并做得好的UNIX口头禅?

无论如何,这是你如何使用传统的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
Run Code Online (Sandbox Code Playgroud)

不是通过使用awk/index()而不是sed和grep,你可以避免需要转义可能出现在你的旧字符串或新字符串中的所有RE元字符,另外还要找出一个字符用作你的sed分隔符' t出现在旧字符串或新字符串中,并且您不需要运行grep,因为替换只会发生在包含所需字符串的文件中.说完所有这些,如果你不想在不修改文件的情况下改变文件时间戳,那么只需在执行mv之前在tmp和原始文件上执行diff,或者在执行之前执行fgrep -q AWK.

警告:以上内容不适用于包含换行符的文件名.如果你有那些,请告诉我们,我们可以告诉你如何处理它们.

  • +1当有人谈到将`sed`与`grep`一起使用时,他们应该只使用`sed`或切换到'awk`.顺便说一句,如果在查找中使用`-print0`,并且(在BASH中)使用`-d\0`应该使用新行来处理文件名. (2认同)