There are many similar posts available on SO. However, the answers given there do not work for me. I am using Mac OS Monterey with version 12.6.5.
I am searching all the files in a directory that contains a pattern or text-part using grep
. Then I want to pipe that filename and use perl -pe
or sed
along with inline option -i
. However, it does not work on my Mac! The minimal code is given below.
newprojectname=NEW
oldprojectname=OLD
dir=.
grep $oldprojectname $dir -lr --exclude test.sh
grep $oldprojectname $dir -lr --exclude test.sh | xargs perl -i -pe's/oldprojectname/$newprojectname/g'
# Also tried
grep $oldprojectname $dir -lr --exclude test.sh | xargs sed -i.'' 's/oldprojectname/$newprojectname/g'
echo `sw_vers`
cat t1.txt t2.txt
Output:
./.test.sh.swp
./t1.txt
./t2.txt
sed: RE error: illegal byte sequence
ProductName: macOS ProductVersion: 12.6.5 BuildVersion: 21G531
OLD
OLD
You can see the grep
operation successfully found two files containing the searched pattern OLD
. However, OLD
has not been replaced by NEW
. So that is the issue here.
I am intentionally keeping the unintentionally generated .swp file so that the answer takes care of binary files as well.
Can this be sorted out with a simple modification to my script?
There are two problems with your script.
First, you've forgotten the $
on $oldprojectname.
... | xargs perl -i -pe's/oldprojectname/$newprojectname/g'
^
there
Second, as pointed out in the comments, with single quotes shell variables won't be interpreted. Perl will literally get s/$oldprojectname/$newprojectname/g'
. For something this simple, use "
.
... | xargs perl -i -pe "s/$oldprojectname/$newprojectname/g"
For something more complicated, perl and shell quoting can get mixed up, they both use $; you could stick with single quotes and export the shell variables as environment variables.
export new=this
expert old=that
... | xargs perl -i -pe 's/$ENV{old}/$ENV{new}/g'
There's no need to first grep for files containing the old text, it's redundant. You can search and replace every file in one pass. Use find
to feed Perl the files.
find $dir -type f -not -name 'test.sh' -print0 | xargs -0 perl -i -pe 's/$old/$new/g'
Files which do not contain $old will be unaffected (verify it doesn't change the modified time, tho).