I think it's got something to do with a syntax detail needed on Windows, but can't test now.
However, as you are in a Perl script, why go out with system
and run another Perl interpreter? It is far more complex and inefficient since it involves a syscall or a shell, and starts another program. Also, it is far harder to get it right -- you need to deal with syntax details, quoting and escaping, for system
, your system's command interpreter, the other instance of Perl, and the regex.
The code below reads the whole file into an array first, which is fine if files aren't huge. In general it is better to process a file line by line, and how to do what you need in that way is discussed in detail in a perlfaq5
page. See the comment at the end, with the link.
use warnings 'all';
use strict;
# your code ...
open my $fh, '<', $file or die "Can't open $file: $!";
my @lines = <$fh>;
# Change @lines in-place. See the comment
s/STRING_TO_BE_REPLACED/$somedir/ for @lines;
open $fh, '>', $file or die "Can't open $file for write: $!";
print $fh @lines;
close $fh;
When we open the $fh
the second time it is closed and re-opened, so there is no need for an explicit close
. When an existing file is opened for writing ('>'
) it is clobbered, so this replaces it.
It's more to write but it is better.
Comment on the in-place change to @lines
This uses the fact that when iterating over an array if we change the index variable, here $_
, the change is made in the original element. The index variable is like an alias for the array element. It says in perlsyn
If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the foreach loop index variable is an implicit alias for each item in the list that you're looping over.
This has the benefit of not copying data and not touching elements that don't change so it is more efficient, potentially a lot more. However, it relies on a subtle property and thus it may be tricky and error prone, so I do not recommend it as a general practice.
To copy the array, with modifications, to a new one
my @lines_new;
foreach my $line (@lines) {
$line =~ s{STRING_TO_BE_REPLACED}{$somedir};
push @lines_new, $line;
}
This also changes @lines
. If it need be kept intact do (my $new_line = $line) =~ s/.../
. Then write @lines_new
to $file
. Somewhere in between these two is
@lines = map { s{STRING_TO_BE_REPLACED}{$somedir}; $_ } @lines;
what was posted originally. However, since the map changes elements of @lines
and copies data to build the output list, while the whole statement also overwrites the array, on reflection I think it makes more sense to do either the in-place change or an explicit copy to a new array.
In principle it is better to not read the whole file at once but rather to process line by line, unless the file is small enough. In that case open the file for reading and new one for writing, and after you copy (with changes) the file over, move the new one to rewrite $file
. See the topic in perlfaq5
The copied file is temporary, to be used to overwrite $file
, so it can be named using the core module File::Temp to avoid accidents. To move a file use move
from the core module File::Copy.