How to find and replace all occurrences of a string recursively in a directory tree?

128,886

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

  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:

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'

Share:
128,886

Related videos on Youtube

Tony
Author by

Tony

Updated on August 25, 2021

Comments

  • Tony
    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
    Tony over 14 years
    Will this replace the string in every file under the /home/user directory? Including sub-directories?
  • vehomzzz
    vehomzzz over 14 years
    Yes it will, though you should specify some sort of pattern -name 'pattern' after find
  • Tony
    Tony over 14 years
    what do you mean "some sort of pattern"?
  • SourceSeeker
    SourceSeeker over 14 years
    A pattern like -name 'pattern' - where "pattern" might be like "domainlist*" - something that narrows down the search.
  • Mansoor Siddiqui
    Mansoor Siddiqui almost 11 years
    As per stackoverflow.com/a/1583282/477451, it's a good idea to use -print0 and -0 on the find and xargs commands respectively.
  • Cthulhu
    Cthulhu about 8 years
    Hello. 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
    akjprajapati almost 7 years
    find /path/to/searchdir/ -name "serachpatter" -type f -maxdepth 4 -mindepth 2 | xargs sed -i 's/stringone/StrIngTwo/g'
  • pat-s
    pat-s almost 7 years
    In 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
    TSG over 6 years
    I would suggest updating the response with the above suggestions. Hopefully that will lead to accept of this answer
  • Revolucion for Monica
    Revolucion for Monica about 6 years
    Thank you for this answer, and how to write it other files in order not to do mistakes ?
  • Pathros
    Pathros about 6 years
    in 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
    dragon788 about 6 years
    Sed supports using an alternate character separator like % that isn't common in URLs so you don't have to escape all the slashes.
  • Timberwolf
    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
    mbx almost 3 years
    Is 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 could sed the backlashes to slashes but an rg options would make things easier in general. For now I use rg "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
    DimiDak over 2 years
    what does the l flag do in -rl ? Cause it's not working without it :)
  • ggorlen
    ggorlen over 2 years
    -l prints the path to each file containing text that matches the pattern.