Linux 命令行全局搜索和替换

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/471183/
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-03 16:54:14  来源:igfitidea点击:

Linux command line global search and replace

linuxsedawkgrep

提问by Michael Kristofik

I'm trying to search and replace a string in all files matched by grep on a linux machine. I've got some pieces of what I want to do, but I'm unsure how best to string them all together.

我正在尝试在 linux 机器上搜索和替换 grep 匹配的所有文件中的字符串。我有一些我想做的事情,但我不确定如何最好地将它们串在一起。

grep -n 'foo' *will give me output in the form:

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

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

For each file returned by grep, I'd like replace "foo" with "bar" and write the result back to the file. Is there a good way to do that? Maybe a fancy pipeline?

对于 grep 返回的每个文件,我想用“bar”替换“foo”并将结果写回文件。有没有好的方法可以做到这一点?也许是一个花哨的管道?

采纳答案by armandino

Do you mean search and replace a string in all files matched by grep?

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

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

Edit

编辑

Since this seems to be a fairly popular question thought I'd update.

由于这似乎是一个相当受欢迎的问题,因此我想我会更新。

Nowadays I mostly use ack-grepas it's more user-friendly. So the above command would be:

现在我主要使用ack-grep它,因为它更用户友好。所以上面的命令是:

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

To handle whitespace in file names you can run:

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

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

you can do more with ack-grep. Say you want to restrict the search to HTML files only:

你可以做更多的事情ack-grep。假设您只想将搜索限制为 HTML 文件:

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

And if white space is not an issue it's even shorter:

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

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

回答by Keltia

If your sed(1)has a -ioption, then use it like this:

如果您sed(1)-i选择,请像这样使用它:

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

If not, there are several ways variations on the following depending on which language you want to play with:

如果没有,根据您要使用的语言,以下内容有多种变体:

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

回答by MattJ

This appears to be what you want, based on the example you gave:

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

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

It is not recursive (it will not descend into subdirectories). For a nice solution replacing in selected files throughout a tree I would use find:

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

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

The *.htmlis the expression that files must match, the .bakafter the -imakes a copy of the original file, with a .bak extension (it can be any extension you like) and the gat the end of the sed expression tells sed to replace multiple copies on one line (rather than only the first one). The -printto find is a convenience to show which files were being matched. All this depends on the exact versions of these tools on your system.

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

回答by hans

I like and used the above solution or a system wide search and replace among thousands of files:

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

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

I assume with the '*.htm?' instead of .html it searches and finds .htm and .html files alike.

我假设使用 '*.htm?' 它搜索并查找 .htm 和 .html 文件而不是 .html。

I replace the .bak with the more system wide used tilde (~) to make clean up of backup files easier.

我将 .bak 替换为系统范围更广的波浪号 (~),以便更轻松地清理备份文件。

回答by koolb

find . -type f -print0 | xargs -0 <sed/perl/ruby cmd>will process multiple space contained file names at once loading one interpreter per batch. Much faster.

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

回答by pymarco

This works using grep without needing to use perl or find.

这可以使用 grep 而无需使用 perl 或 find。

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

回答by jlevy

The answer already given of using findand sed

已经给出了使用findsed的答案

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

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

is probably the standard answer. Or you could use perl -pi -e s/foo/bar/g'instead of the sedcommand.

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

For most quick uses, you may find the command rplis easier to remember. Here is replacement (foo -> bar), recursively on all files in the current directory:

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

rpl -R foo bar .

rpl -R foo bar .

It's not available by default on most Linux distros but is quick to install (apt-get install rplor similar).

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

However, for tougher jobs that involve regular expressions and back substitution, or file renames as well as search-and-replace, the most general and powerful tool I'm aware of is repren, a small Python script I wrote a while back for some thornier renaming and refactoring tasks. The reasons you might prefer it are:

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

  • Support renaming of files as well as search-and-replace on file contents (including moving files between directories and creating new parent directories).
  • See changes before you commit to performing the search and replace.
  • Support regular expressions with back substitution, whole words, case insensitive, and case preserving (replace foo -> bar, Foo -> Bar, FOO -> BAR) modes.
  • Works with multiple replacements, including swaps (foo -> bar and bar -> foo) or sets of non-unique replacements (foo -> bar, f -> x).
  • 支持重命名文件以及搜索和替换文件内容(包括在目录之间移动文件和创建新的父目录)。
  • 在提交执行搜索和替换之前查看更改。
  • 支持正则表达式,具有反向替换、整个单词、不区分大小写和大小写保留(替换 foo -> bar、Foo -> Bar、FOO -> BAR)模式。
  • 适用于多种替换,包括交换(foo -> bar 和 bar -> foo)或非唯一替换集(foo -> bar, f -> x)。

Check the READMEfor examples.

检查 README以获取示例。

回答by siliconrockstar

This is actually easier than it seems.

这实际上比看起来容易。

grep -Rl 'foo' ./ | xargs -n 1 -I % sh -c "ls %; sed -i 's/foo/bar/g' %";
  • grep recurses through your tree (-R) and prints just the file name (-l), starting at the current directory (./)
  • that gets piped to xargs, which processes them one at a time (-n 1), and uses % as a placeholder (-I %) in a shell command (sh -c)
  • in the shell command, first the file name is printed (ls %;)
  • then sed does an inline operation (-i), a substution('s/') of foo with bar (foo/bar), globally (/g) on the file (again, represented by %)
  • grep 遍历您的树 (-R) 并仅打印文件名 (-l),从当前目录 (./) 开始
  • 它通过管道传输到 xargs,它一次处理一个 (-n 1),并在 shell 命令 (sh -c) 中使用 % 作为占位符 (-I %)
  • 在shell命令中,首先打印文件名(ls %;)
  • 然后 sed 执行内联操作 (-i),将 foo 替换为 ('s/') 和 bar (foo/bar),对文件进行全局 (/g)(同样,由 % 表示)

Easy peasy. If you get a good grasp on find, grep, xargs, sed, and awk, almost nothing is impossible when it comes to text file manipulation in bash :)

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