如何在目录树中递归地查找和替换字符串的所有匹配项?

只使用 grep 和 sed,如何替换所有出现的:

a.example.com

b.example.com

/home/user/目录树下的文本文件中递归地查找和替换子目录中所有文件中的所有匹配项。

133714 次浏览

Try this:

find /home/user/ -type f | xargs sed -i  's/a\.example\.com/b.example.com/g'

In case you want to ignore dot directories

find . \( ! -regex '.*/\..*' \) -type f | xargs sed -i 's/a\.example\.com/b.example.com/g'

Edit: escaped dots in search expression

I know this is a really old question, but...

  1. @vehomzzz's answer uses find and xargs when the questions says explicitly grep and sed only.

  2. @EmployedRussian and @BrooksMoses tried to say it was a dup of awk and sed, but it's not - again, the question explicitly says grep and sed only.

So here is my solution, assuming you are using Bash as your shell:

OLDIFS=$IFS
IFS=$'\n'
for f in `grep -rl a.example.com .` # Use -irl instead of -rl for case insensitive search
do
sed -i 's/a\.example\.com/b.example.com/g' $f # Use /gi instead of /g for case insensitive search
done
IFS=$OLDIFS

If you are using a different shell, such as Unix SHell, let me know and I will try to find a syntax adjustment.

P.S.: Here's a one-liner:

OLDIFS=$IFS;IFS=$'\n';for f in `grep -rl a.example.com .`;do sed -i 's/a\.example\.com/b.example.com/g' $f;done;IFS=$OLDIFS

Sources:

Try this command:

/home/user/ directory - find ./ -type f \
-exec sed -i -e 's/a.example.com/b.example.com/g' {} \;

Try this:

grep -rl 'SearchString' ./ | xargs sed -i 's/REPLACESTRING/WITHTHIS/g'

grep -rl will recursively search for the SEARCHSTRING in the directories ./ and will replace the strings using sed.

Ex:

Replacing a name TOM with JERRY using search string as SWATKATS in directory CARTOONNETWORK

grep -rl 'SWATKATS' CARTOONNETWORK/ | xargs sed -i 's/TOM/JERRY/g'

This will replace TOM with JERRY in all the files and subdirectories under CARTOONNETWORK wherever it finds the string SWATKATS.

The command below will search all the files recursively whose name matches the search pattern and will replace the string:

find /path/to/searchdir/ -name "serachpatter" -type f | xargs sed -i 's/stringone/StrIngTwo/g'

Also if you want to limit the depth of recursion you can put the limits as well:

find /path/to/searchdir/ -name "serachpatter" -type f -maxdepth 4 -mindepth 2 | xargs sed -i 's/stringone/StrIngTwo/g'

it is much simpler than that.

for i in `find *` ; do sed -i -- 's/search string/target string/g' $i; done

find i => will iterate over all the files in the folder and in subfolders.

sed -i => will replace in the files the relevant string if exists.

For me works the next command:

find /path/to/dir -name "file.txt" | xargs sed -i 's/string_to_replace/new_string/g'

if string contains slash 'path/to/dir' it can be replace with another character to separate, like '@' instead '/'.

For example: 's@string/to/replace@new/string@g'

On macOS, none of the answers worked for me. I discovered that was due to differences in how sed works on macOS and other BSD systems compared to GNU.

In particular BSD sed takes the -i option but requires a suffix for the backup (but an empty suffix is permitted)

grep version from this answer.

grep -rl 'foo' ./ | LC_ALL=C xargs sed -i '' 's/foo/bar/g'

find version from this answer.

find . \( ! -regex '.*/\..*' \) -type f | LC_ALL=C xargs sed -i '' 's/foo/bar/g'

Don't omit the Regex to ignore . folders if you're in a Git repo. I realized that the hard way!

That LC_ALL=C option is to avoid getting ABC1 if sed finds a byte sequence that is not a valid UTF-8 character. That's another difference between BSD and GNU. Depending on the kind of files you are dealing with, you may not need it.

For some reason that is not clear to me, the grep version found more occurrences than the find one, which is why I recommend to use grep.

We can try using the more powerful ripgrep as

rg "BYE_BYE_TEXT" ./ --files-with-matches | xargs sed -i "s/BYE_BYE_TEXT/WELCOME_TEXT/g"

Because ripgrep is good at finding and sed is great at replacing.