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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6178498/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 00:58:58  来源:igfitidea点击:

Using grep and sed to find and replace a string

linuxshellunixsedgrep

提问by Michael

I am using the following to search a directory recursively for specific string and replace it with another:

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

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

This works okay. The only problem is that if the string doesn't exist then sedfails because it doesn't get any arguments. This is a problem for me since i'm running this automatically with ANT and the build fails since sedfails.

这工作正常。唯一的问题是,如果字符串不存在,则sed失败,因为它没有任何参数。这对我来说是一个问题,因为我使用 ANT 自动运行它并且构建sed失败了。

Is there a way to make it fail-proof in case the string is not found?

如果找不到字符串,是否有办法使其防故障?

I'm interested in a one line simple solution I can use (not necessarily with grepor sedbut with common unix commands like these).

我对我可以使用的单行简单解决方案感兴趣(不一定使用grepsed但使用这些常见的 unix 命令)。

采纳答案by Michael Berkowski

You can use findand -execdirectly into sedrather than first locating oldstrwith grep. It's maybe a bit less efficient, but that might not be important. This way, the sedreplacement is executed over all files listed by find, but if oldstrisn't there it obviously won't operate on it.

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

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

回答by geekosaur

Standard xargshas no good way to do it; you're better off using find -execas someone else suggested, or wrap the sedin a script which does nothing if there are no arguments. GNU xargshas the --no-run-if-emptyoption, and BSD / OS X xargshas the -Loption which looks like it should do something similar.

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

回答by phoxis

If you are to replace a fixed string or some pattern, I would also like to add the bash builtin pattern string replacement variable substitution construct. Instead of describing it myself, I am quoting the section from the bash manual:

如果您要替换固定字符串或某种模式,我还想添加 bash 内置模式字符串替换变量替换构造。我没有自己描述它,而是引用 bash 手册中的部分:

${parameter/pattern/string}

The pattern is expanded to produce a pattern just as in pathname expansion. parameteris expanded and the longest match of patternagainst its value is replaced with string. If patternbegins with /, all matches of patternare replaced with string. Normally only the first match is replaced. If patternbegins with #, it must match at the beginning of the expanded value of parameter. If patternbegins with %, it must match at the end of the expanded value of parameter. If stringis null, matches of patternare deleted and the /following patternmay be omitted. If parameteris @or *, the substitution operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameteris an array variable subscripted with @or *, the substitution operation is applied to each member of the array in turn, and the expansion is the resultant list.

${parameter/pattern/string}

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

回答by phoxis

I think that without using -execyou can simply provide /dev/nullas at least one argument in case nothing is found:

我认为不使用-exec你可以简单地提供/dev/null至少一个参数,以防万一没有找到:

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

回答by Michael

I have taken Vlad's idea and changed it a little bit. Instead of

我采纳了 Vlad 的想法并对其进行了一些更改。代替

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

Which yields

哪个产量

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

I'm doing in 3 different connections to the remote server

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

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

Although this is less elegant and requires 2 more connections to the server (maybe there's a way to do it all in one line) it does the job efficiently as well

虽然这不太优雅并且需要 2 个以上的服务器连接(也许有一种方法可以在一行中完成所有工作),但它也能有效地完成工作

回答by jm666

Your solution is ok. only try it in this way:

你的解决方案没问题。只能这样试试:

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

so execute the xargsonly when grep return 0, e.g. when found the string in some files.

所以xargs只在 grep 返回时执行0,例如在某些文件中找到字符串时。

回答by neo7

My use case was I wanted to replace foo:/Drive_Letterwith foo:/bar/baz/xyzIn my case I was able to do it with the following code. I was in the same directory location where there were bulk of files.

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

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

hope that helped.

希望有所帮助。