Ответ 1
Последние версии File::Slurp
предоставляют удобные функции edit_file
и edit_file_lines
. Внутренняя часть вашего кода будет выглядеть так:
use File::Slurp qw(edit_file);
edit_file { s/$oldPass/$newPass/g } $filename;
У меня есть программа, которая имеет несколько имен файлов, настроенных внутри. Программа редактирует набор файлов конфигурации, связанных с учетной записью базы данных, а затем изменяет пароль базы данных для учетной записи базы данных.
Список файлов конфигурации связан с именем учетной записи базы данных через внутренний список. Когда я обрабатываю эти файлы, в моей программе появляется следующий цикл:
BEGIN { $^I = '.oldPW'; } # Enable in-place editing
...
foreach (@{$Services{$request}{'files'}})
{
my $filename = $Services{$request}{'configDir'} . '/' . $_;
print "Processing ${filename}\n";
open CONFIGFILE, '+<', $filename or warn $!;
while (<CONFIGFILE>)
{
s/$oldPass/$newPass/;
print;
}
close CONFIGFILE;
}
Проблема в том, что это записывает измененный вывод в STDOUT, а не в CONFIGFILE. Как я могу получить это на самом деле редактировать на месте? Переместить $ ^ я внутри цикла? Печатать КОНФИФИЛЬ? Я в тупике.
>
Обновление: я нашел то, что искал на PerlMonks. Вы можете использовать локальный ARGV внутри цикла для редактирования на месте обычным способом Perl. Вышеуказанный цикл теперь выглядит так:
foreach (@{$Services{$request}{'files'}})
{
my $filename = $Services{$request}{'configDir'} . '/' . $_;
print "Processing ${filename}\n";
{
local @ARGV = ( $filename);
while (<>)
{
s/$oldPass/$newPass/;
print;
}
}
}
Если бы это не было связано с настройкой configDir в начале, я мог бы просто выбросить весь список в локальный @ARGV, но это достаточно эффективно.
Спасибо за полезные предложения по Tie::File
. Я бы, наверное, пошел по этому пути, если бы закончил. Файлы конфигурации, которые я редактирую, никогда не превышают нескольких килобайт, поэтому Tie не будет использовать слишком много памяти.
Последние версии File::Slurp
предоставляют удобные функции edit_file
и edit_file_lines
. Внутренняя часть вашего кода будет выглядеть так:
use File::Slurp qw(edit_file);
edit_file { s/$oldPass/$newPass/g } $filename;
Переменная $^I
работает только с последовательностью имен файлов, хранящихся в $ARGV
, используя пустую конструкцию <>
. Возможно, что-то вроде этого будет работать:
BEGIN { $^I = '.oldPW'; } # Enable in-place editing
...
local @ARGV = map {
$Services{$request}{'configDir'} . '/' . $_
} @{$Services{$request}{'files'}};
while (<>) {
s/$oldPass/$newPass/;
# print? print ARGVOUT? I don't remember
print ARGVOUT;
}
но если это не простой script и вам нужны @ARGV
и STDOUT
для других целей, вам, вероятно, лучше использовать что-то вроде Tie::File
для этой задачи:
use Tie::File;
foreach (@{$Services{$request}{'files'}})
{
my $filename = $Services{$request}{'configDir'} . '/' . $_;
# make the backup yourself
system("cp $filename $filename.oldPW"); # also consider File::Copy
my @array;
tie @array, 'Tie::File', $filename;
# now edit @array
s/$oldPass/$newPass/ for @array;
# untie to trigger rewriting the file
untie @array;
}
Tie:: File уже упоминался и очень прост. Избежать переключения -i, вероятно, хорошая идея для сценариев без командной строки. Если вы хотите избежать Tie:: File, стандартное решение таково:
В основном это происходит за кулисами с помощью переключателя -i.bak, но с дополнительной гибкостью.