如何grep和替换

Posted

技术标签:

【中文标题】如何grep和替换【英文标题】:How to grep and replace 【发布时间】:2013-03-02 10:02:15 【问题描述】:

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

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

grep 'string_to_find' -r ./*

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

【问题讨论】:

我不相信 grep 可以做到这一点(我可能是错的)。更简单的方法是使用 sed 或 perl 进行替换 尝试使用sed -i 's/.*substring.*/replace/' @Eddy_Em 这将用替换替换整行。您需要使用分组来捕获子字符串之前和之后的行部分,然后将其放入替换行中。 sed -i 's/\(.*\)substring\(.*\)/\1replace\2/' Using grep and sed to find and replace a string的可能重复 @see ***.com/questions/5171901/… 【参考方案1】:

其他解决方案混合了正则表达式语法。要将 perl/PCRE 模式用于两者搜索和替换,并且只处理匹配的文件,这非常有效:

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

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

翻译:grep递归并列出匹配的文件名,每个都用nul分隔以保护任何特殊字符;将任何文件名通过管道传输到xargs,它需要一个以 nul 分隔的列表;如果收到任何文件名,请将它们传递给 perl 以执行实际替换。

对于 区分大小写 匹配,从 grep 中删除 i 标志并从 s/// 表达式中删除 i 模式修饰符,但 不是来自perl 本身的i 标志。从grep 中删除I 标志以包含二进制文件。

【讨论】:

Perl 本身非常有能力递归文件结构。事实上,有一个工具 find2perl 随 Perl 一起提供,它可以在没有任何 xargs 诡计的情况下完成这类事情。 @tripleee find 不搜索文件内容,重点是只处理匹配文件而不编写 Perl 程序。 这是一个很好的 Windows 解决方案,因为它避免了基于 sed 的解决方案转换行尾的问题。谢谢!【参考方案2】:

你甚至可以这样做:

示例

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

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

【讨论】:

grep 仅在存在不应修改的文件时才有用。在所有文件上运行sed 将更新文件的修改日期,但如果没有匹配项则保持内容不变。 @tripleee: 注意...但是[sed]如果没有匹配则保持内容不变"。使用-i时,我相信sed改变它接触的每个文件的文件时间,即使内容没有改变。sed 也转换行结尾。我不在 Windows 上的 Git repo 中使用sed,因为所有CRLF 都更改为LF . 这个命令需要在 -i 后面加一个 "" 来表示在原地替换发生后不会生成备份文件,至少在 macosx 中是这样。查看手册页以获取详细信息。如果您需要备份,请在此处放置要创建的文件的扩展名。 对于空格是文件名,您需要在 grep 和 xargs 上执行 NULL 终止。 ***.com/questions/17296525/… 我喜欢用 Linux 替换 Windows 的有趣微妙的潜意识命令【参考方案3】:

我会这样做:

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

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

不过,如果您想省略在文件名中查找带有 filename 字符串的特定文件,则不要简单地这样做:

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

这将执行与上述相同的操作,但对在/path/to/dir 下找到的所有文件。

【讨论】:

【参考方案4】:

在 git repo 中使用 findsed 时要非常小心!如果您不排除二进制文件,则可能会出现此错误:

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

要解决此错误,您需要通过将new_string 替换为old_string 来恢复sed。这将还原您替换的字符串,因此您将回到问题的开头。

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

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

@hobs 的学分

【讨论】:

【参考方案5】:

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

.bashrc(或任何地方)中启用shopt -s globstar 允许** glob 模式以递归方式匹配所有子目录和文件。

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

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

如果您想在扩展名为 .ext 的所有子文件中将 SEARCH 替换为 REPLACE,globstar 还允许您执行 sed -i 's/SEARCH/REPLACE/g' **/*.ext 之类的操作。

【讨论】:

“另一种选择是将 perl 与 globstar 一起使用...” - 不在 Posixy 机器上,例如 Solaris。这就是为什么我专门寻找grepsed【参考方案6】:

这在 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 命令缺少最后的斜杠,这是语法错误。 为什么sort -u 甚至是其中的一部分?在什么情况下,您希望grep -rl 生成两次相同的文件名?【参考方案7】:

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

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

【讨论】:

在 OS X 10.10 终端上,参数-i 的正确扩展字符串是必需的。例如,find /path/to/files -type f -exec sed -i "" "s/oldstring/new string/g" \; 不管怎样,提供空字符串仍然会创建一个备份文件,这与手册中的描述不同... 为什么会出现“sed: RE 错误:非法字节序列”。是的,我为 OS X 添加了-i ""。否则它可以工作。 我在 macOS 10.12 上遇到了非法字节序列问题,这个问题/答案解决了我的问题:***.com/questions/19242275/…。 这会触及每个文件,因此会修改文件时间;并在 Windows 上将行尾从 CRLF 转换为 LF【参考方案8】:

我得到了答案。

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

【讨论】:

这将扫描匹配的文件两次...一次使用grep,然后再次使用sed。使用find 方法效率更高,但您提到的这种方法确实有效。 在 OS X 上,您需要将 sed -i 's/str1/str2/g' 更改为 sed -i "" 's/str1/str2/g' 才能正常工作。 @cmevoli 使用这种方法,grep 会遍历所有文件,sed 只扫描与grep 匹配的文件。使用另一个答案中的find 方法,find 首先列出所有文件,然后sed 将扫描该目录中的所有文件。所以这个方法不一定慢,它取决于有多少匹配,以及sedgrepfind之间搜索速度的差异。 OTOH 这种方式可以让您在实际替换之前预览 grep 发现的内容,大大降低失败的风险,特别是对于像我这样的正则表达式 n00bs 当您的 grep 替换比 sed 更聪明时,这也很有用。例如 ripgrep 服从 .gitignore 而 sed 不服从。【参考方案9】:

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

【讨论】:

我认为这可能是完成工作的最简单方法。在不需要时强制 grep 是不必要的。

以上是关于如何grep和替换的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Find / Grep / Sed 在文件中查找和替换字符串

用grep替换R中的“Q”和“Q”

使用Grep与Xargs和进程替换

命令行:在 grep 匹配的所有文件名中搜索和替换

grep 或 find 和 sed 替换字符串

使用 grep 和 sed 在 shell 中查找和替换同一文件中的多行