2011-05-19 56 views
14

我有一个程序有一些内部配置的文件名。该程序编辑一组与数据库帐户关联的配置文件,然后更改数据库帐户的数据库密码。配置文件列表通过内部列表与数据库帐户的名称关联。当我处理这些文件,我在我的节目以下循环:需要perl就地编辑不在命令行上的文件

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; 
} 

的问题是,这种写入修改后的输出到标准输出,而不是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的有用建议,如果这样做可能会这样。我正在编辑的配置文件永远不会超过几K字节,所以Tie不会使用太多的内存。

回答

10

$^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; 
} 
+0

其实我不需要备份,因为这些文件存储在源代码控制系统中。此脚本的下一步是将SCCS(本例中为Perforce)集成到脚本中,以便在更新文件时检出并提交文件。 – Wexxor 2011-05-19 22:48:09

15

最近的File::Slurp版本提供了便利的功能,edit_fileedit_file_lines。你的代码的内部看起来如下:

use File::Slurp qw(edit_file); 
edit_file { s/$oldPass/$newPass/g } $filename; 
+2

不要忘记备份你的文件。 – mob 2011-05-19 20:32:19

2

Tie :: File已经被提及,并且非常简单。对于非命令行脚本,避免使用-i开关可能是一个好主意。如果你正在寻找避免领带::文件,标准的解决方案是这样的:

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

无论如何,这本质上是与-i.bak开关在幕后发生的,但具有更大的灵活性。