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

問題は、変更された出力が CONFIGFILE ではなく STDOUT に書き込まれることです。これを実際にその場で編集するにはどうすればよいですか? $^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。これをやり直せば、私はおそらくそのようになるでしょう。私が編集している構成ファイルの長さが数 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;
}

ただし、単純なスクリプトではなく、他の目的で必要な@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;
}
于 2011-05-19T19:42:53.807 に答える
2

Tie::File は既に言及されており、非常に単純です。-i スイッチを避けることは、非コマンドライン スクリプトの場合、おそらく良い考えです。Tie::File を避けたい場合、標準的な解決策は次のとおりです。

  • 入力用にファイルを開く
  • 出力用の一時ファイルを開く
  • 入力ファイルから 1 行読み取ります。
  • 好きなように行を変更します。
  • 新しい行を一時ファイルに書き出します。
  • 次の行などにループします。
  • 入力ファイルと出力ファイルを閉じます。
  • ファイル名に .bak を追加するなど、入力ファイルの名前を何らかのバックアップ名に変更します。
  • 一時出力ファイルの名前を元の入力ファイル名に変更します。

これは基本的に、 -i.bak スイッチを使用して舞台裏で行われていることですが、柔軟性が追加されています。

于 2011-05-19T22:13:58.920 に答える