2011-05-19 55 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開關在幕後發生的,但具有更大的靈活性。