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?
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' {} \;
I got the answer.
grep -rl matchstring somedir/ | xargs sed -i 's/string1/string2/g'
You could even follow 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.
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
Other solutions mix regex syntaxes. To use perl/PCRE patterns for both search and replace, and avoid processing every file, this works quite well:
grep -rlZP 'match1' | xargs -0r perl -pi -e 's/match2/replace/g;'
where match1
and match2
are usually identical but match1
may be simplified to remove more advanced features that are only relevant to the substitution, e.g. capturing groups.
Translation: grep recursively and list matching files, separated by nul to protect any special characters in the filename, that match this PCRE pattern, then pipe those filenames to xargs which is expecting a null-separated list, but won't do anything if no names are received, and get perl to rewrite each file, substituting lines where matches are found.
Add the I
option to grep
to ignore binary files, too.
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'
.
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
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
.