2

I have changed up my director structure and I want to do the following:

  • Do a recursive grep to find all instances of a match
  • Change to the updated location string

One example (out of hundreds) would be:

from common.utils import debug  --> from etc.common.utils import debug

To get all the instances of what I'm looking for I'm doing:

$ grep -r 'common.' ./

However, I also need to make sure common is preceded by a space. How would I do this find and replace?

4

3 回答 3

4

It's hard to tell exactly what you want because your refactoring example changes the import as well as the package, but the following will change common. -> etc.common. for all files in a directory:

sed -i 's/\bcommon\./etc.&/' $(egrep -lr '\bcommon\.' .)

This assumes you have gnu sed available, which most linux systems do. Also, just to let you know, this will fail if there are too many files for sed to handle at one time. In that case, you can do this:

egrep -lr '\bcommon\.' . | xargs sed -i 's/\bcommon\./etc.&/'

Note that it might be a good idea to run the sed command as sed -i'.OLD' 's/\bcommon\./etc.&/' so that you get a backup of the original file.

于 2012-06-03T02:01:49.080 回答
1

This is roughly how you would do it using find. This requires testing

 find . -name \*.java -exec sed "s/FIND_STR/REPLACE_STR/g" {}

This translates as "Starting from the current directory find all files that end in .java and execute sed on the file (where {} is a place holder for the currently found file) "s/FIND_STR/REPLACE_STR/g" replaces FIND_STR with REPLACE_STR in each line in the current file.

于 2012-06-03T01:56:03.967 回答
1

If your grep implementation supports Perl syntax (-P flag, on e.g. Linux it's usually available), you can benefit from the additional features like word boundaries:

$ grep -Pr '\bcommon\.'

By the way:

grep -r tends to be much slower than a previously piped find command as in Rob's example. Furthermore, when you're sure that the file-names found do not contain any whitespace, using xargs is much faster than -exec:

$ find . -type f -name '*.java' | xargs grep -P '\bcommon\.'

Or, applied to Tim's example:

$ find . -type f -name '*.java' | xargs sed -i.bak 's/\<common\./etc.common./'

Note that, in the latter example, the replacement is done after creating a *.bak backup for each file changed. This way you can review the command's results and then delete the backups:

$ find . -type f -name '*.bak' | xargs rm

If you've made an oopsie, the following command will restore the previous versions:

$ find . -type f -name '*.bak' | while read LINE; do mv -f $LINE `basename $LINE`; done

Of course, if you aren't sure that there's no whitespace in the file names and paths, you should apply the commands via find's -exec parameter.

Cheers!

于 2012-06-03T02:11:01.730 回答