如何grep和替换

bil*_*ian 228 linux grep replace

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

我知道找到它的命令可能如下所示:

grep 'string_to_find' -r ./*
Run Code Online (Sandbox Code Playgroud)

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

rez*_*ter 223

另一个选择是使用find然后通过sed传递它.

find /path/to/files -type f -exec sed -i 's/oldstring/new string/g' {} \;
Run Code Online (Sandbox Code Playgroud)

  • 在OS X 10.10终端上,需要一个适当的扩展字符串到参数`-i`.例如,`find/path/to/files -type f -exec sed -i"""s/oldstring/new string/g"{} \;`无论如何,提供空字符串仍会创建备份文件,这与手册中描述的不同... (32认同)
  • 为什么我会得到"sed:RE错误:非法字节序列".是的,我为OS X添加了`-i""`.否则它会起作用. (9认同)
  • 我在macOS 10.12上遇到了非法字节序列问题,这个问题/答案解决了我的问题:http://stackoverflow.com/questions/19242275/re-error-illegal-byte-sequence-on-mac-os-x. (2认同)
  • 这涉及到每个文件,因此文件时间被修改;并在Windows上将行尾从CRLF转换为LF。 (2认同)

bil*_*ian 158

我得到了答案.

grep -rl matchstring somedir/ | xargs sed -i 's/string1/string2/g'
Run Code Online (Sandbox Code Playgroud)

  • 在OS X上,您需要将`sed -i'/ str1/str2/g'`更改为`sed -i""'s/str1/str2/g'`才能生效. (36认同)
  • 这将扫描匹配的文件两次...一次用`grep`然后再用`sed`扫描.使用`find`方法更有效,但你提到的这种方法确实有效. (13认同)
  • @cmevoli使用这种方法,`grep`遍历所有文件,`sed`只扫描`grep`匹配的文件.在另一个答案中使用`find`方法,`find`首先列出所有文件,然后`sed`将扫描该目录中的所有文件.所以这个方法不一定慢,它取决于有多少匹配以及`sed`,`grep`和`find`之间的搜索速度的差异. (6认同)
  • OTOH这种方式可以让你预览grep在实际替换之前发现了什么,大大降低了失败的风险,特别是像我这样的正则表达式n00bs (3认同)
  • 当您的 grep 替换比 sed 更聪明时,这也很有用。例如 ripgrep 服从 .gitignore 而 sed 不。 (3认同)
  • @jdf为什么会这样? (2认同)

Dul*_*sta 36

你甚至可以这样跟着..

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'
Run Code Online (Sandbox Code Playgroud)

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

  • 只有存在不应修改的文件时,`grep`才有用.在所有文件上运行`sed`将更新文件的修改日期,但如果没有匹配则保持内容不变. (2认同)
  • @tripleee:小心 *...但是 [sed] 如果没有匹配项,则保留内容不变”*。当使用 `-i` 时,我相信 `sed` 会更改它所触及的每个文件的文件时间,即使内容没有改变。`sed` 也会转换行结尾。我不在 Windows 上的 Git 存储库中使用 `sed`,因为所有 `CRLF` 都更改为 `LF`。 (2认同)

Mar*_*hli 28

这在OS X上最适合我:

grep -r -l 'searchtext' . | sort | uniq | xargs perl -e "s/matchtext/replacetext/" -pi
Run Code Online (Sandbox Code Playgroud)

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

  • 为什么`sort -u`甚至是其中的一部分?在什么情况下你会期望`grep -rl`产生两次相同的文件名? (3认同)

min*_*ret 5

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


Wal*_*alf 5

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

grep -rlZP 'match1' | xargs -0r perl -pi -e 's/match2/replace/g;'
Run Code Online (Sandbox Code Playgroud)

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

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

还添加了忽略二进制文件的I选项grep


tsv*_*iko 5

在 git 存储库中使用find和使用时要非常小心!sed如果不排除二进制文件,可能会出现以下错误:

error: bad index file sha1 signature 
fatal: index file corrupt
Run Code Online (Sandbox Code Playgroud)

要解决此错误,您需要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")
Run Code Online (Sandbox Code Playgroud)

@hobs 的学分