15

我有一个内部配置了许多文件名的程序。该程序编辑一堆与数据库帐户关联的配置文件,然后更改数据库帐户的数据库密码。

配置文件列表通过内部列表与数据库帐户的名称相关联。当我处理这些文件时,我的程序中有以下循环:

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 移动到循环内?打印配置文件?我难住了。

>

更新:在 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 不会占用太多内存。

4

3 回答 3

16

最新版本File::Slurp提供了方便的功能,edit_file并且edit_file_lines. 您的代码的内部部分如下所示:

use File::Slurp qw(edit_file);
edit_file { s/$oldPass/$newPass/g } $filename;
于 2011-05-19T20:01:33.863 回答
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;
}

但如果它不是一个简单的脚本并且您需要@ARGVSTDOUT用于其他目的,那么您最好使用类似的东西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;
}
于 2011-05-19T19:42:53.807 回答
2

Tie::File 已经提到过,非常简单。对于非命令行脚本,避免使用 -i 开关可能是一个好主意。如果您想避免 Tie::File,标准解决方案是:

  • 打开文件进行输入
  • 打开一个临时文件进行输出
  • 从输入文件中读取一行。
  • 以您喜欢的任何方式修改该行。
  • 将新行写入您的临时文件。
  • 循环到下一行,等等。
  • 关闭输入和输出文件。
  • 将输入文件重命名为某个备份名称,例如将 .bak 附加到文件名。
  • 将临时输出文件重命名为原始输入文件名。

无论如何,这基本上是使用 -i.bak 开关在幕后发生的事情,但增加了灵活性。

于 2011-05-19T22:13:58.920 回答