在目录及其子目录中使用sed查找并替换

322

我运行了这个命令,在我的站点根目录中查找并替换所有出现的 'apple' 为 'orange':

find ./ -exec sed -i 's/apple/orange/g' {} \;

但它不会遍历子目录。

这个命令有什么问题?

以下是find ./的一些输出行:

./index.php
./header.php
./fpd
./fpd/font
./fpd/font/desktop.ini
./fpd/font/courier.php
./fpd/font/symbol.php

1
你怎么知道它不处理子目录? - carlpett
因为它会出现以下错误:sed:无法编辑./fpd:不是常规文件 sed:无法编辑./fpd/font:不是常规文件 sed:无法编辑./fpd/font/makefont:不是常规文件 - hd.
哦...我用grep查找了苹果,但什么都没找到。它们全都被替换了。;) 谢谢你。你让我看到了新世界!!! - hd.
可能是Awk/Sed:如何递归查找/替换字符串?的重复问题。 - tripleee
如果使用zsh,您可以使用例如src/**/.js - alex
显示剩余4条评论
8个回答

556

为避免将目录名称传递给sed,您的find应该像这样:

find ./ -type f -exec sed -i -e 's/apple/orange/g' {} \;

26
为了使这个命令生效,你可能需要把 sed -i 's/apple/orange/g' 改成 sed -i '' 's/apple/orange/g'。请注意不要改变原意。 - paulmelnikow
10
-i需要一个参数:用于保存临时文件的扩展名。在GNU sed中,-i和其参数之间似乎没有空格,但在BSD sed中有空格...因此BSD的-i'' 's/foo/bar/'相当于GNU的-i 's/foo/bar/' - paulmelnikow
29
实际上,在 Mac OS 上添加“-e”并不起作用。在执行上述命令后,使用“touch a b c d e”会产生以下目录列表:“a a-e b b-e c c-e d d-e e e-e”。 - paulmelnikow
10
针对Mac OS的问题,这个回答涉及到https://dev59.com/C2Ik5IYBdhLWcg3we-H5中的`RE error: illegal byte sequence`问题。 - kakoma
5
对于使用fish shell的用户,请确保引用空花括号'{}',因为如果不加引号,fish会自动扩展空花括号。 - Kevin Cherepski
显示剩余6条评论

130

对于更大的搜索和替换任务,使用grep和xargs会更好、更快,例如:

grep -rl 'apples' /dir_to_search_under | xargs sed -i 's/apples/oranges/g'

35
谢谢你的回答,它非常有帮助!如果在一个 Git 仓库中,使用 git grep -l 'apples' | xargs sed -i 's/apples/oranges/g' 甚至更快。 - mrodrigues
2
如果在macOS上,请使用xargs sed -i'' 's/apples/oranges/g' - cacti5
2
如果您想要替换一个包含正斜杠的内容,可以使用 sed 命令并将 / 替换为 |,例如 ... xargs sed -i 's|mduuid/apples|mduuid/oranges|g'。参考链接:https://dev59.com/LlkR5IYBdhLWcg3w3A0M - NessDan
这在xargs 4.8.0中失败了。它会产生错误消息 "unmatched single quote: by defaults quotes are special to xargs unless you use the -0 option"。 - bob

14

因为也有 macOS 用户阅读此文(就像我一样),以下代码对我有效(在 10.14 上)

egrep -rl '<pattern>' <dir> | xargs -I@ sed -i '' 's/<arg1>/<arg2>/g' @

在 macOS 上,使用-i-e的所有其他答案都无效。

来源


在 Mac 上,接受的答案确实可以[有点]工作 - 但它会输出带有<original-filename>-e的“重复”文件,这些文件需要被删除/管道传输到另一个命令中(以逐字使用)。虽然这种方法更好,但对我仍然有效(在11.2.3上)。 - bob dylan

6
这是我的解决方法:
find ./ -type f -exec sed -i '' 's#NEEDLE#REPLACEMENT#' *.php {} \;

原始问题并不限于 *.php 文件,还有一个 .ini 文件。 - knocte
2
未引用的 *.php 是错误的;你只是幸运地没有在起始目录中展开它,因为你碰巧没有匹配的文件。 - tripleee
1
find命令中,可以使用-name *.php来获取文件名的限制。 - aleric

4
grep -e apple your_site_root/**/*.* -s -l | xargs sed -i "" "s|apple|orange|"

1

发现了一个非常好的程序,叫做 ruplacer

https://github.com/dmerejkowsky/ruplacer

用法

ruplacer before_text after_text # prints out list of things it will replace
ruplacer before_text after_text --go # executes the replacements

它还尊重 .gitignore,因此不会破坏您的 .gitnode_modules 目录(默认情况下,find . 将进入您的 .git 目录并可能损坏它!!!)

0

我觉得我们可以用一行简单的命令完成这个。

for i in `grep -rl eth0 . 2> /dev/null`; do sed -i ‘s/eth0/eth1/’ $i; done

请参考此页面


-7
在Linux操作系统中:
sed -i 's/textSerch/textReplace/g' namefile

如果“sed”无法工作,请尝试:
perl -i -pe 's/textSerch/textReplace/g' namefile

4
他希望能够查找所有子目录中包含该字符串的文件并进行替换,而不仅仅是单个文件。 - phuclv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接