billtian
billtian

Reputation: 6831

How to search and replace using grep

I need to recursively search for a specified string within all files and subdirectories within a directory and replace this string with another string.

I know that the command to find it might look like this:

grep 'string_to_find' -r ./*

But how can I replace every instance of string_to_find with another string?

Upvotes: 371

Views: 564290

Answers (10)

Kpym
Kpym

Reputation: 4023

Modern rust tools can be used to do this job. For example to replace in all (non ignored) files "oldstring" and "oldString" with "newstring" and "newString" respectively you can :

Use fd and sd

fd -tf -x sd 'old([Ss]tring)' 'new$1' {}

Use ned

ned -R -p 'old([Ss]tring)' -r 'new$1' .

Use ruplacer

ruplacer --go 'old([Ss]tring)' 'new$1' .

Ignored files

To include ignored (by .gitignore) and hidden files you have to specify it :

  • use -IH for fd,
  • use --ignored --hiddenfor ruplacer.

Upvotes: 0

Walf
Walf

Reputation: 9279

Other solutions mix regex syntaxes. To use perl/PCRE patterns for both search and replace, and process only matching files, this works quite well:

grep -rlIZPi 'match1' | xargs -0r perl -pi -e 's/match2/replace/gi;'

match1 and match2 are usually identical but match2 can contain more advanced features that are only relevant to the substitution, e.g. capturing groups.

Translation: grep recursively and list matching filenames, each separated by null to protect any special characters; pipe any filenames to xargs which is expecting a null-separated list; if any filenames are received, pass them to perl to perform the actual substitutions.

For case-sensitive matching, drop the i flag from grep and the i pattern modifier from the s/// expression, but not the i flag from perl itself. To include binary files, remove the I flag from grep.

Upvotes: 8

Du-Lacoste
Du-Lacoste

Reputation: 12757

You could even do it like this:

Example

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'

This will search for the string 'windows' in all files relative to the current directory and replace 'windows' with 'linux' for each occurrence of the string in each file.

Upvotes: 75

tinnick
tinnick

Reputation: 386

Here is what I would do:

find /path/to/dir -type f -iname "*filename*" -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

this will look for all files containing filename in the file's name under the /path/to/dir, than for every file found, search for the line with searchstring and replace old with new.

Though if you want to omit looking for a specific file with a filename string in the file's name, than simply do:

find /path/to/dir -type f -print0 | xargs -0 sed -i '/searchstring/s/old/new/g'

This will do the same thing above, but to all files found under /path/to/dir.

Upvotes: 1

tsveti_iko
tsveti_iko

Reputation: 7952

Be very careful when using find and sed in a git repo! If you don't exclude the binary files you can end up with this error:

error: bad index file sha1 signature 
fatal: index file corrupt

To solve this error you need to revert the sed by replacing your new_string with your old_string. This will revert your replaced strings, so you will be back to the beginning of the problem.

The correct way to search for a string and replace it is to skip find and use grep instead in order to ignore the binary files:

sed -ri -e "s/old_string/new_string/g" $(grep -Elr --binary-files=without-match "old_string" "/files_dir")

Credits for @hobs

Upvotes: 6

GuiltyDolphin
GuiltyDolphin

Reputation: 768

Another option would be to just use perl with globstar.

Enabling shopt -s globstar in your .bashrc (or wherever) allows the ** glob pattern to match all sub-directories and files recursively.

Thus using perl -pXe 's/SEARCH/REPLACE/g' -i ** will recursively replace SEARCH with REPLACE.

The -X flag tells perl to "disable all warnings" - which means that it won't complain about directories.

The globstar also allows you to do things like sed -i 's/SEARCH/REPLACE/g' **/*.ext if you wanted to replace SEARCH with REPLACE in all child files with the extension .ext.

Upvotes: -1

Marc Juchli
Marc Juchli

Reputation: 2278

This works best for me on OS X:

grep -r -l 'searchtext' . | sort | uniq | xargs perl -e "s/matchtext/replacetext/" -pi

Source: http://www.praj.com.au/post/23691181208/grep-replace-text-string-in-files

Upvotes: 42

rezizter
rezizter

Reputation: 5148

Another option is to use find and then pass it through sed.

find /path/to/files -type f -exec sed -i 's/oldstring/new string/g' {} \;

Upvotes: 324

billtian
billtian

Reputation: 6831

I got the answer.

grep -rl matchstring somedir/ | xargs sed -i 's/string1/string2/g'

Upvotes: 260

minopret
minopret

Reputation: 4806

Usually not with grep, but rather with sed -i 's/string_to_find/another_string/g' or perl -i.bak -pe 's/string_to_find/another_string/g'.

Upvotes: 8

Related Questions