89

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.

Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
Tony
  • 935
  • 1
  • 7
  • 3

9 Answers9

128

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

Ovidiu
  • 131
  • 1
  • 11
vehomzzz
  • 42,832
  • 72
  • 186
  • 216
  • 1
    Will this replace the string in every file under the /home/user directory? Including sub-directories? – Tony Oct 18 '09 at 15:46
  • 1
    Yes it will, though you should specify some sort of pattern -name 'pattern' after find – vehomzzz Oct 18 '09 at 15:51
  • 1
    what do you mean "some sort of pattern"? – Tony Oct 18 '09 at 15:54
  • 1
    A pattern like `-name 'pattern'` - where "pattern" might be like "domainlist*" - something that narrows down the search. – Dennis Williamson Oct 18 '09 at 19:40
  • 10
    As per http://stackoverflow.com/a/1583282/477451, it's a good idea to use `-print0` and `-0` on the `find` and `xargs` commands respectively. – Mansoor Siddiqui Jul 30 '13 at 16:40
  • 1
    I would suggest updating the response with the above suggestions. Hopefully that will lead to accept of this answer – TSG Jan 15 '18 at 18:41
  • 1
    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 ... – Pathros Mar 20 '18 at 18:56
47

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.

Richard
  • 56,349
  • 34
  • 180
  • 251
Abhinav Deshpande
  • 569
  • 1
  • 6
  • 5
14

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.

mokagio
  • 16,391
  • 3
  • 51
  • 58
7

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:

Community
  • 1
  • 1
Timberwolf
  • 426
  • 4
  • 13
  • 1
    Sed supports using an alternate character separator like `%` that isn't common in URLs so you don't have to escape all the slashes. – dragon788 Apr 02 '18 at 19:30
  • 1
    @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. – Timberwolf Apr 04 '18 at 04:55
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'

Yahor M
  • 617
  • 8
  • 8
4

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.

Rounak Datta
  • 442
  • 7
  • 10
  • 1
    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). – mbx Jun 18 '21 at 09:32
3

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.

kohane15
  • 809
  • 12
  • 16
gilad 1
  • 79
  • 2
2

Try this command:

/home/user/ directory - find ./ -type f \
-exec sed -i -e 's/a.example.com/b.example.com/g' {} \;
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
Navneet
  • 347
  • 1
  • 10
  • 6
    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. – Cthulhu Mar 14 '16 at 15:56
1

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'

pat-s
  • 5,992
  • 1
  • 32
  • 60
akjprajapati
  • 138
  • 2
  • 9
  • 1
    find /path/to/searchdir/ -name "serachpatter" -type f -maxdepth 4 -mindepth 2 | xargs sed -i 's/stringone/StrIngTwo/g' – akjprajapati Jul 24 '17 at 17:31
  • 2
    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. – pat-s Jul 24 '17 at 17:59