search and replace in a directory
The last couple days I’ve been refactoring a project’s codebase. I would
like to replace every instance of the string findvar
in any file with
find_var
to make the naming scheme more consistent.
grep --files-with-matches -r "findvar" * | xargs sed -e "s/findvar/find_var/g" -i ""
This is not the safest, most efficient, or most elegant way to do this, but it’s the way that I’m most likely to remember.
Other Ways
I could do this by individually opening and editing the following files:
$ grep -r "findvar" * | cut -f 1 -d ":" | uniq
R/apply.R
R/canon.R
R/subset.R
R/utils.R
man/findvar.Rd
tests/testthat/test_utils.R
As a side note, the grep
command above can be done more efficiently,
since grep
can stop searching a file as soon as it finds a single match.
This might be handy if you’re searching very large files and relatively few
of them don’t match the expression.
$ grep --files-with-matches -r "findvar" *
Now that we’ve found the files sed
(streaming editor) can edit them all
simultaneously:
grep --files-with-matches -r "findvar" * | xargs sed -e "s/findvar/find_var/g" -i "back"
xargs
takes the list of files produced by grep
and passes them to sed
as the list of files. If we instead piped the grep
output directly to
sed
then sed
would perform the substitution on the file names, which
isn’t what we want here.
The -i "back"
argument makes a backup copy of each file by adding
"back"
to each file name before modifying the files in place. Often I
use -i ""
to modify the files in place without backups from within a Git
repository, since Git can easily recover the files when I make a mistake.
The command above is redundant because we search for findvar
twice.
Instead we can just process the files we’re interested in, those with file
extensions .R
and Rmd
. man/findvar.Rd
is automatically generated so
I don’t need to process it.
find -E . -regex ".*\.R(md)?" | xargs sed -e "s/findvar/find_var/g" -i ""
The -E
flag says to use extended regular expressions. These are the
modern regexes that people are probably more familiar with.
It’s less important that I only match .R
and .Rmd
files and more
important that I do not attempt to process files inside the .git
directory. The following command lists all the files except those in the
.git
directory:
find . \! -path "./.git/*" -type f
This doesn’t work to combine with sed
because I end up passing .png
image files which contain illegal byte sequences for a regex.