命令行:搜索并替换所有与grep匹配的文件名


80

我正在尝试搜索和替换grep匹配的所有文件中的字符串:

grep -n 'foo' * 将以以下形式给我输出:

[filename]:[line number]:[text]

对于通过的grep返回每个文件,我想通过替换,修改文件foobar

Answers:


70

您的意思是搜索并替换所有与grep匹配的文件中的字符串吗?

perl -p -i -e 's/oldstring/newstring/g' `grep -ril searchpattern *`

编辑

由于这似乎是一个很受欢迎的问题,所以我想更新一下。

如今,我主要使用ack-grep它,因为它更加用户友好。因此,上面的命令将是:

perl -p -i -e 's/old/new/g' `ack -l searchpattern`

要处理文件名中的空格,可以运行:

ack --print0 -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'

您可以使用进行更多操作ack-grep。假设您只想将搜索范围限制为HTML文件:

ack --print0 --html -l searchpattern | xargs -0 perl -p -i -e 's/old/new/g'

如果空格不是问题,它甚至会更短:

perl -p -i -e 's/old/new/g' `ack -l --html searchpattern`
perl -p -i -e 's/old/new/g' `ack -f --html` # will match all html files

3
我认为这可能与包含空格的文件/文件夹有关。Can't open Untitled: No such file or directory, <> line 5尝试“无标题的文件夹/file.txt”时。
Xeoncross

108

根据您提供的示例,这似乎是您想要的:

sed -i 's/foo/bar/g' *

它不是递归的(不会下降到子目录中)。对于替换整个树中选定文件的解决方案,我可以使用find:

find . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

*.html是,文件必须符合的表情,.bak之后-i使原始文件的一个副本,其中一个.bak扩展名(它可以是任何扩展你等)和g在sed的表达年底告诉sed替换多个副本一行(而不是仅第一行)。该-print发现是一个方便的表演哪些文件被被匹配。所有这些取决于系统上这些工具的确切版本。


1
对cygwin用户的警告。find和sed组合似乎更改了流过的文件的用户权限。可以使用命令chmod -R 644 *从查找/ sed操作时使用的同一目录级别简单地解决此问题。
kaskelotti

警告那些不想使用-i参数的人:如果您不使用它,它将不起作用(不要问我为什么)
knocte

4
@knocte -i告诉sed修改文件,否则它仅将修改后的版本打印到stdout。如果您不希望创建.bak文件,只需省略'.bak'部分,-i也可以独立工作。
MattJ 2013年

2
在OSX上,您需要为find命令提供一个目录,例如从find . -name '*.html'find directoryname/ -name '*'
2014年

我需要添加一个-e,否则它以为'...'部分是后缀sed -ie 's/foo/bar/g' *
Vish

14

如果您sed(1)可以-i选择,请按以下方式使用它:

for i in *; do
  sed -i 's/foo/bar/' $i
done

如果没有,则根据您要使用的语言,有以下几种变种:

ruby -i.bak -pe 'sub(%r{foo}, 'bar')' *
perl -pi.bak -e 's/foo/bar/' *

2
for i in *; do ...sed是多余的,sed可以将文件列表作为参数。
詹斯2012年

2
@Jens为什么不通过在上面的示例中添加您自己的示例来改善此答案?

变量应该总是被引用for i in *; do; sed -i 's/foo/bar/g' "$i"; done

6

我喜欢并使用上述解决方案,或者在数千个文件中进行系统范围内的搜索和替换:

find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' {} \;

我假设使用“ * .htm?” 它搜索并找到.htm和.html文件,而不是.html。

我将.bak替换为更多系统范围内使用的波浪号(〜),以简化备份文件的清理。


4

使用grep可以工作,而无需使用perl或find。

grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @

xargs在OSX或BSD没有-i openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man1 / ...您是要使用大写的“ I”吗?
托尼·亚当斯

我不知道-i不适用于其他操作系统。在ubuntu上为我工作。
pymarco 2014年

我的xargs联机帮助页(在Ubuntu上)说-i已弃用,请-I改用。因此,我们应该说:grep -rli 'old-word' * | xargs -I filepath sed -i 's/old-word/new-word/g' filepath
Max Wallace

3

find . -type f -print0 | xargs -0 <sed/perl/ruby cmd>将立即处理多个空间包含的文件名,每批加载一个解释器。快多了。


@knocte,“ cmd”是整个搜索和替换命令的标记,无论选择哪种给定工具。该答案回答了有关如何处理包含的空白文件名的问题。
Tony Adams

1

使用findsed已经给出的答案

find -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

这可能是标准答案。或者您可以使用perl -pi -e s/foo/bar/g'代替sed命令。

对于大多数快速使用,您可能会发现命令rpl更容易记住。这是当前目录中所有文件的递归替换(foo-> bar):

rpl -R foo bar .

在大多数Linux发行版中,默认情况下它不可用,但是可以快速安装(apt-get install rpl或类似版本)。

但是,对于涉及正则表达式和后向替换,文件重命名以及搜索和替换的艰巨工作,我知道最通用,最强大的工具是repren,这是我为一些代码编写的一个小型Python脚本棘手的重命名和重构任务。您可能更喜欢的原因是:

  • 支持文件重命名以及文件内容的搜索和替换(包括在目录之间移动文件和创建新的父目录)。
  • 在确认执行搜索和替换之前,请先查看更改。
  • 支持带有反向替换,整个单词,不区分大小写和保留大小写的正则表达式(替换foo-> bar,Foo-> Bar,FOO-> BAR)模式。
  • 适用于多种替换,包括交换(foo-> bar和bar-> foo)或一组非唯一替换(foo-> bar,f-> x)。

查看自述文件中的示例。


1

这实际上比看起来容易。

grep -Rl 'foo' ./ | xargs -n 1 -I % sh -c "ls %; sed -i 's/foo/bar/g' %";
  • grep遍历您的树(-R),并仅打印文件名(-l),从当前目录(./)开始
  • 它通过管道传递给xargs,xargs一次处理它们(-n 1),并在shell命令(sh -c)中使用%作为占位符(-I%)
  • 在shell命令中,首先打印文件名(ls%;)
  • 然后sed进行内联运算(-i),将foo替换为bar(foo / bar),然后在文件上全局(/ g)进行替换(再次用%表示)

十分简单。如果您对find,grep,xargs,sed和awk有了很好的了解,那么在bash中进行文本文件操作时几乎没有什么不可能的:)


ah,无视,这个pymarco已经涵盖了以上内容。我的答案留给我解释。
Siliconrockstar
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.