I am trying to match and replace in multiple files some string using
local $/;
open(FILE, "<error.c");
$document=<FILE>;
close(FILE);
$found=0;
while($document=~s/([a-z_]+)\.h/$1_new\.h/gs){
$found=$found+1;
};
open(FILE, ">error.c");
print FILE "$document";
close(FILE);'
It enters an endless loop, because the result of the substitution is matched again by the regular expression searched for. But shouldn't this be avoided by the s///g
construct?
EDIT:
I found that also a foreach
loop will not do exactly what I want (it will replace all occurrences, but print only one of them). The reason seems to be that the perl substitution and and search behave quite differently in the foreach()
and while()
constructs. To have a solution to replace in multiple files which outputs also all individual replacements, I came up with the following body:
# mandatory user inputs
my @files;
my $subs;
my $regex;
# additional user inputs
my $fileregex = '.*';
my $retval = 0;
my $opt_testonly=0;
foreach my $file (@files){
print "FILE: $file\n";
if(not($file =~ /$fileregex/)){
print "filename does not match regular expression for filenames\n";
next;
}
# read file
local $/;
if(not(open(FILE, "<$file"))){
print STDERR "ERROR: could not open file\n";
$retval = 1;
next;
};
my $string=<FILE>;
close(FILE);
my @locations_orig;
my @matches_orig;
my @results_orig;
# find matches
while ($string =~ /$regex/g) {
push @locations_orig, [ $-[0], $+[0] ];
push @matches_orig, $&;
my $result = eval("\"$subs\"");
push @results_orig, $result;
print "MATCH: ".$&." --> ".$result." @[".$-[0].",".$+[0]."]\n";
}
# reverse order
my @locations = reverse(@locations_orig);
my @matches = reverse(@matches_orig);
my @results = reverse(@results_orig);
# number of matches
my $length=$#matches+1;
my $count;
# replace matches
for($count=0;$count<$length;$count=$count+1){
substr($string, $locations[$count][0], $locations[$count][1]-$locations[$count][0]) = $results[$count];
}
# write file
if(not($opt_testonly) and $length>0){
open(FILE, ">$file"); print FILE $string; close(FILE);
}
}
exit $retval;
It first reads the file creates lists of the matches, their positions and the replacement text in each file (printing each match). Second it will replace all occurrences starting from the end of the string (in order not to change the position of previous messages). Finally, if matches were found, it writes the string back to the file. Can surely be more elegant, but it does what I want.