How to grep and replace
LinuxGrepReplaceLinux Problem Overview
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?
Linux Solutions
Solution 1 - Linux
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' {} \;
Solution 2 - Linux
I got the answer.
grep -rl matchstring somedir/ | xargs sed -i 's/string1/string2/g'
Solution 3 - Linux
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.
Solution 4 - Linux
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
Solution 5 - Linux
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'
.
Solution 6 - Linux
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
.
Solution 7 - Linux
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
Solution 8 - Linux
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
.
Solution 9 - Linux
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 :
fd and sd
Usefd -tf -x sd 'old([Ss]tring)' 'new$1' {}
ned
Usened -R -p 'old([Ss]tring)' -r 'new$1' .
ruplacer
Useruplacer --go 'old([Ss]tring)' 'new$1' .
Ignored files
To include ignored (by .gitignore
) and hidden files you have to specify it :
- use
-IH
forfd
, - use
--ignored --hidden
forruplacer
.
Solution 10 - Linux
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
.