使用sed和grep/egrep进行搜索和替换

102

我正在使用egrep -R命令,后跟包含约10个联合的正则表达式,例如:.jpg | .png | .gif等。这很有效,现在我想用.bmp替换找到的所有字符串。

我考虑了以下方法:

egrep -lR "\.jpg|\.png|\.gif" . | sed "s/some_expression/.jpg/" file_it_came_form

所以这里的问题是如何在sed中执行类似的联合正则表达式,并告诉它将更改保存到输入它的文件中。


2
我在搜索目录层次结构中跨多个文件进行搜索和替换的方法时发现了这个问题。对于其他处于我的情况下的人,请尝试使用rpl - titaniumdecoy
谢谢RPL的工作,它真的很容易记住...只需使用命令"rpl old_string new_string target_files"即可。 - cesarpachon
6个回答

195

使用以下命令:

egrep -lRZ "\.jpg|\.png|\.gif" . \
    | xargs -0 -l sed -i -e 's/\.jpg\|\.gif\|\.png/.bmp/g'
  • egrep:使用扩展正则表达式查找匹配的行

    • -l:仅列出匹配文件名

    • -R:递归搜索所有给定目录

    • -Z:使用\0作为记录分隔符

    • ".jpg|\.png|\.gif":匹配字符串之一".jpg",".gif"或".png"

    • .:从当前目录开始搜索

  • xargs:使用标准输入作为参数执行命令

    • -0:使用\0作为记录分隔符。这对于匹配egrep-Z并避免受到输入文件名中的空格和换行符的干扰非常重要。

    • -l:使用每个命令一行作为参数

  • sed:流编辑器

    • -i:替换输入文件而不进行备份

    • -e:使用以下参数作为表达式

    • 's/\.jpg\|\.gif\|\.png/.bmp/g':将所有出现的字符串".jpg",".gif"或".png"替换为".bmp"


我发现这个命令会在处理的所有文件末尾添加一个换行符。 - titaniumdecoy
@titanumdecoy:我无法重现这种行为。你使用的sed版本和操作系统是什么? - David Schmitt
1
@DavidSchmitt:你可能想使用sed -r来进行扩展正则表达式。此时,该模式将匹配在egrep中使用的内容,并且您可能希望将其放入变量中以供重用。 - bukzor
这个命令让我省了好几个小时的时间,因为我不用再手动复制我的应用程序中的头文件到我所创建的库中。太棒了 :) 这是我使用的命令:egrep -lRZ ".h$" . | xargs -0 tar -cvf headers.tar | (cp headers.tar headers; cd headers; tar xf headers.tar;) - The Lazy Coder
1
在OSX上,您必须提供一个空文件给sed -i,例如:sed -i'',以便正确地进行原地更改。 - atripes
显示剩余2条评论

11

另一种方法是这样做

find . -name *.xml -exec sed -i "s/4.6.0-SNAPSHOT/5.0.0-SNAPSHOT/" {} \;

关于上述命令的一些帮助

find 命令将在由 . 指示的当前目录中为你查找。

-name 参数可以使用通配符指定文件名,对我来说是 pom.xml。

-exec 执行操作。

sed 是流编辑器。

-i 忽略大小写。

s 用于替换。

/4.6.0.../ 要搜索的字符串。

/5.0.0.../ 要替换的字符串。


1
也许不如那个答案强大,但对我来说更容易理解。 - icc97
1
@icc97 - 我同意这样更容易理解,但我想不出为什么这会更弱。 - dgo

11

说实话,尽管我喜欢使用sed来处理合适的任务,但这明显是perl的任务--对于这种一行代码的任务,perl更加强大,特别是将其“写回到来源处”(perl的-i开关可以为您完成此操作,并且还可以选择保留旧版本,例如附加了.bak的版本,只需使用-i.bak即可)。

perl -i.bak -pe 's/\.jpg|\.png|\.gif/.jpg/

不要在sed中进行复杂的工作(如果可能的话),也不要使用awk...


6
sed 使用 -i 选项,就像 perl 一样。 - Stobor
@Stobor - 我发誓我曾经遇到过问题,当我将正则表达式替换字符串传递给perl操作时,它做了我想要的事情,不像sed,即使我给了sed正则表达式选项。我认为我可能忘记了一些sed标志或者它有一些限制。 - meder omuraliev

5

我无法在此页面上成功运行任何命令:使用 sed 解决方案 会给所有处理的文件添加一个换行符,而使用 perl 解决方案 则无法从 find 命令中接受足够的参数。我找到了这个完美解决方案:

find . -type f -name '*.[hm]' -print0 
    | xargs -0 perl -pi -e 's/search_regex/replacement_string/g'

这将递归遍历当前目录树,并在以.h.m结尾的任何文件中替换search_regexreplacement_string
我过去也使用过rpl来实现此目的。

0

尝试使用for循环来做些什么

 for i in `egrep -lR "YOURSEARCH" .` ; do echo  $i; sed 's/f/k/' <$i >/tmp/`basename $i`; mv /tmp/`basename $i` $i; done

不太好看,但应该可以。


3
在这里使用xargs肯定更合适。 - Nathan Fellman
1
使用 |while read i 模式可以启用流式传输,并避免当 egrep 的结果变得太长时出现长度限制。 - David Schmitt

0
我的使用情况是我想要将foo:/Drive_Letter替换为foo:/bar/baz/xyz。在我的情况下,我能够使用以下代码完成它。我在相同的目录位置,有大量的文件。
find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'

希望有所帮助。

更新 s|foo:/Drive_letter:|foo:/ba/baz/xyz|g


您可以使用其他分隔符来执行sed命令,这样做可以使路径名更加美观:sed 's|foo:/Drive_letter:|foo:/ba/baz/xyz|g' - Kevin

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