使用grep和sed查找和替换字符串


69

我正在使用以下内容以递归方式在目录中搜索特定的字符串,并将其替换为另一个:

grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g'

这样可以。唯一的问题是,如果字符串不存在,则sed失败,因为它没有任何参数。这对我来说是个问题,因为我正在使用ANT自动运行此操作,并且由于sed失败而导致构建失败。

如果找不到该字符串,有没有办法使它失效?

我对我可以使用的单行简单解决方案感兴趣(不一定与grepsed与此类通用unix命令一起使用)。


我想使其尽可能简单的原因是因为我的脚本连接到远程服务器并使用SSH运行此行。如果要为此使用Shell脚本,则必须先将Shell脚本复制到服务器,然后再在其中运行。我试图避免它,并保持简单。谢谢。
迈克尔


1
从Google来到这里,我正在寻找问题中给出的确切字符串!(我没有OP所遭受的问题)
LondonRob

Answers:


74

您可以使用find-exec直接进入sed,而不是第一个定位oldstrgrep。它的效率可能较低,但这可能并不重要。这样,sed替换将在列出的所有文件上执行find,但如果oldstr不存在,则显然无法对其进行操作。

find /path -type f -exec sed -i 's/oldstr/newstr/g' {} \;

@Vlad:是的,因为您要sed为每个文件运行一个单独的文件,而不是让xargs它们批处理。就是说,除非您要谈论数千个微小文件,否则您不太可能会注意到差异。
geekosaur 2011年

@geekosaur:哦,对..我还没有考虑过应该制作许多execs shell。好点子!

谢谢,我尝试了您的解决方案。它确实解决了我的返回值问题,现在当找不到任何东西时返回0。问题是它返回:sed: no input files即使我在目录中有带有oldstring的文件。知道为什么 ?
迈克尔

2
我的问题是:每个文件的时间戳都将重置为“立即”,就好像它们都被“触摸”一样,即使文件中未进行任何编辑。
pbarill

1
我已经看到了在各处使用find的“ Hello world”示例,但是没有什么可以限制它在每个文件(包括二进制文件)上运行sed。我尚未找到有关管道的有效示例,并且我尝试了多种方法来破解非常有限的实用程序的简化版本。
杰里·米勒

11

您的解决方案还可以。仅以这种方式尝试:

files=$(grep -rl oldstr path) && echo $files | xargs sed....

因此xargs只有在grep return时才执行0,例如在某些文件中找到该字符串时。


2
这是一个很好的低估解决方案!这是我在grep文件输出列表上执行sed所需要的,避免出现错误:“无法读取...。没有这样的文件或目录”。感谢您的回答!
Leopoldo Sanczyk 2014年

2
同意@Leopoldo。这看起来像是最Posixy和最可移植的(对于像Solaris这样的OS),并且副作用最少(不会在mtime上碰到每个文件,等等)。files=$(grep -rl oldstr path | cut -f 1 -d ':' | sort | uniq)应该建立一个较小的列表,其中文件仅列出一次。
jww

8

我接受了弗拉德的想法,并对其进行了一些修改。代替

grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' /dev/null

哪个产量

sed: couldn't edit /dev/null: not a regular file

我正在与远程服务器建立3种不同的连接

touch deleteme
grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' ./deleteme
rm deleteme

尽管这不太优雅,并且需要与服务器建立2个以上的连接(也许有一种方法可以全部完成一条连接),但它也可以高效地完成工作


这个脚本拯救了我的生命!
workdreamer

7

标准xargs没有做到这一点的好方法。您最好find -exec按照其他人的建议使用,或将其包装sed在脚本中,如果没有参数,该脚本不执行任何操作。GNUxargs具有该--no-run-if-empty选项,而BSD / OS Xxargs具有该-L选项,看起来它应该执行类似的操作。


谢谢,我尝试使用--no-run-if-empty,但是它仍然返回非零代码(返回1),这也会触发构建失败。find命令有多通用?
迈克尔

find -exec回到第7研究版UNIX;它应该可以在任何已find安装的地方工作。
geekosaur 2011年

1
+1令我惊讶的xargs -r是,更多答案中未提及该修复程序。对于这种有限的用例,它是简单而足够的。
13年

4

我认为如果不使用它,-exec您只需提供/dev/null至少一个参数即可,以防万一。

grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' /dev/null

2
可能是一个非常整洁的解决方法。可悲的是我得到了sed: couldn't edit /dev/null: not a regular file:)
迈克尔

@michael:结果-运行这样的事情实际上会因为过多的参数而使shell崩溃...参见superuser.com/questions/257250/…–

我的目录中有很多文件,但是只有少数包含我要更改的字符串。因此,我不必担心太多有关sed的争论。
迈克尔

@michael:那里列出的解决方案只有10票,应该可以完成工作。

1

我的用例是我想替换为 foo:/Drive_Letterfoo:/bar/baz/xyz 我的情况,我可以使用以下代码来实现。我在有大量文件的相同目录位置。

find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

希望能有所帮助。


0

不确定这是否会有所帮助,但您可以将其与远程服务器一起使用,如下面的示例

ssh example.server.com“找到/ DIR_NAME-类型f-名称” FILES_LOOKING_FOR“ -exec sed -i's / LOOKINGFOR / withThisString / g'{};”

用服务器替换example.server.com用目录/文件位置替换DIR_NAME用要查找的文件替换FILES_LOOKING_FOR用要寻找的文件替换LOOKINGFOR用要替换的ThisString替换要替换的文件


-1

如果要替换固定字符串或某些模式,我还想添加bash内置模式字符串替换变量替换构造。我不是自己描述它,而是引用bash手册中的这一节:

${parameter/pattern/string}

与路径名扩展一样,扩展模式以生成模式。 扩展参数,并将模式与其值的最长匹配项替换为 string。如果 模式 开头/,所有的比赛模式被替换为的字符串。通常只替换第一个比赛。如果 pattern 以开头 #,则它必须在参数的扩展值的开头匹配 。如果pattern以开头%,则它必须在参数的扩展值的末尾匹配。如果模式字符串为空,匹配项将被删除,/以下模式可以省略。如果 parameter@*,则将替换操作依次应用于每个位置参数,并且扩展为结果列表。如果parameter是下标为@或的数组变量*,则将替换操作依次应用于数组的每个成员,并且扩展为结果列表。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.