我有一个程序,内部配置了许多文件名。该程序编辑与数据库帐户关联的一组配置文件,然后更改数据库帐户的数据库密码。配置文件列表通过内部列表与数据库帐户的名称相关联。当我处理这些文件时,我的程序中有以下循环:
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。如何让它实际编辑到位?在循环中移动$ ^ I?打印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不会占用太多内存。
答案 0 :(得分:16)
File::Slurp
的最新版本提供了方便的功能edit_file
和edit_file_lines
。代码的内部部分将是:
use File::Slurp qw(edit_file);
edit_file { s/$oldPass/$newPass/g } $filename;
答案 1 :(得分:11)
$^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;
}
答案 2 :(得分:2)
Tie :: File已被提及,而且非常简单。对于非命令行脚本,避免使用-i开关可能是一个好主意。如果您想避免使用Tie :: File,标准解决方案就是:
这基本上是使用-i.bak开关在幕后进行的,但具有更大的灵活性。