我有一个程序,它在内部配置了许多文件名。程序编辑与数据库帐户关联的一堆配置文件,然后更改数据库帐户的数据库密码。
配置文件列表通过内部列表与数据库帐户的名称相关联。当我处理这些文件时,我的程序中有以下循环:
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,而不是配置文件。我如何让它实际就地编辑?将 $^I 移到循环内?打印配置文件?我被难住了。
>
更新:我在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
的有用建议。如果这样做,我可能会这样做。我正在编辑的配置文件的长度永远不会超过几 KB,因此 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;
}
但是,如果它不是一个简单的脚本,并且您需要@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;
}
:File 已经提到过,而且非常简单。 对于非命令行脚本,避免使用 -i 开关可能是一个好主意。 如果你想避免Tie::File,标准的解决方案是这样的:
- 打开文件进行输入
- 打开临时文件进行输出
- 从输入文件中读取一行。
- 以您喜欢的任何方式修改该行。
- 将新行写出到临时文件中。
- 循环到下一行,等等。
- 关闭输入和输出文件。
- 将输入文件重命名为某些备份名称,例如将.bak附加到文件名。
- 将临时输出文件重命名为原始输入文件名。
无论如何,这本质上就是 -i.bak 开关在幕后发生的事情,但增加了灵活性。