81

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

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

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

对于 grep 返回的每个文件,我想通过替换为来修改foo文件bar

4

9 回答 9

108

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 . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

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

于 2009-01-23T01:41:57.227 回答
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
于 2009-01-22T22:57:36.293 回答
14

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

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/' *
于 2009-01-22T22:59:28.007 回答
6

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.

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

于 2010-07-20T18:14:03.427 回答
4

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

grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @
于 2013-12-21T16:58:47.337 回答
3

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.

于 2011-12-01T13:40:56.393 回答
1

The answer already given of using find and sed

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 sed command.

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

rpl -R foo bar .

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

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:

  • 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).

Check the README for examples.

于 2015-03-22T06:15:52.003 回答
1

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 %)

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 :)

于 2015-11-19T03:35:32.500 回答
0

2022 answer.
https://github.com/BurntSushi/ripgrep/blob/master/FAQ.md#how-can-i-search-and-replace-with-ripgrep

rg foo --files-with-matches | xargs sed -i 's/foo/bar/g'

will replace all instances of 'foo' with 'bar' in the files in which ripgrep finds the foo pattern. The -i flag to sed indicates that you are editing files in place, and s/foo/bar/g says that you are performing a substitution of the pattern foo for bar, and that you are doing this substitution globally (all occurrences of the pattern in each file).

于 2022-01-13T09:14:37.660 回答