How to find and replace all occurrences of a string recursively in a directory tree?
Solution 1
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
Solution 2
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
.
Solution 3
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 sed: RE error: illegal byte sequence
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
.
Solution 4
I know this is a really old question, but...
@vehomzzz's answer uses
find
andxargs
when the questions says explicitlygrep
andsed
only.@EmployedRussian and @BrooksMoses tried to say it was a dup of
awk
andsed
, but it's not - again, the question explicitly saysgrep
andsed
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:
Solution 5
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'
Related videos on Youtube
Tony
Updated on August 25, 2021Comments
-
Tony over 2 years
Using just grep and sed, how do I replace all occurrences of:
a.example.com
with
b.example.com
within a text file under the
/home/user/
directory tree recursively finding and replacing all occurrences in all files in sub-directories as well. -
Tony over 14 yearsWill this replace the string in every file under the /home/user directory? Including sub-directories?
-
vehomzzz over 14 yearsYes it will, though you should specify some sort of pattern -name 'pattern' after find
-
Tony over 14 yearswhat do you mean "some sort of pattern"?
-
SourceSeeker over 14 yearsA pattern like
-name 'pattern'
- where "pattern" might be like "domainlist*" - something that narrows down the search. -
Mansoor Siddiqui almost 11 yearsAs per stackoverflow.com/a/1583282/477451, it's a good idea to use
-print0
and-0
on thefind
andxargs
commands respectively. -
Cthulhu about 8 yearsHello. Please don't just dump code as an answer. Explain what you are doing so users can understand how to solve the problem. Cheers.
-
akjprajapati almost 7 yearsfind /path/to/searchdir/ -name "serachpatter" -type f -maxdepth 4 -mindepth 2 | xargs sed -i 's/stringone/StrIngTwo/g'
-
pat-s almost 7 yearsIn the future, please edit your answer instead of posting a comment beneath it. Also, please format code appropriate; do not use bold formatted text for code.
-
TSG over 6 yearsI would suggest updating the response with the above suggestions. Hopefully that will lead to accept of this answer
-
Revolucion for Monica about 6 yearsThank you for this answer, and how to write it other files in order not to do mistakes ?
-
Pathros about 6 yearsin my case it searched and replaced in the whole system, even if i did type the directory. Why? Not that bad but it may not be so good in some other cases ...
-
dragon788 about 6 yearsSed supports using an alternate character separator like
%
that isn't common in URLs so you don't have to escape all the slashes. -
Timberwolf about 6 years@dragon788 I did not know that, however I wasn't escaping the forward slashes. I was escaping the periods. A period is a regex special character for any character, thus it has to be escaped to match specifically the period in a domain name.
-
mbx almost 3 yearsIs there an option to make this work under windows as well (git bash if you're wondering)?
rg
keeps using backslashes, sed does not like that (mostly omits them since none matches to an escape code in my case, I guess) and fails with No such file or directoy. I couldsed
the backlashes to slashes but anrg
options would make things easier in general. For now I userg "inpattern" ./ --files-with-matches | sed "s|\\\|/|g" | xargs sed -i -b "s/inpattern/outpattern/g"
- (-b
/binary mode to keep your line endings as they are). -
DimiDak over 2 yearswhat does the
l
flag do in-rl
? Cause it's not working without it :) -
ggorlen over 2 years
-l
prints the path to each file containing text that matches the pattern.