需要 Perl 就地编辑文件而不是在命令行上

发布于 2024-11-08 16:44:31 字数 1271 浏览 0 评论 0原文

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

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

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 不会使用太多内存。

I have a program that has a number of filenames configured internally. The program edits a bunch of configuration files associated with a database account, and then changes the database password for the database account.

The list of configuration files is associated with the name of the database account via an internal list. When I process these files, I have the following loop in my program:

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

The problem is, this writes the modified output to STDOUT, not CONFIGFILE. How do I get this to actually edit inplace? Move the $^I inside the loop? Print CONFIGFILE? I'm stumped.

>

Update: I found what I was looking for on PerlMonks. You can use a local ARGV inside the loop to do inplace editing in the normal Perl way. The above loop now looks like:

foreach (@{$Services{$request}{'files'}})
{
    my $filename = $Services{$request}{'configDir'} . '/' . $_;
    print "Processing ${filename}\n";
    {
        local @ARGV = ( $filename);
        while (<>)
        {
            s/$oldPass/$newPass/;
            print;
        }
    }
}

If it weren't for tacking the configDir on the beginning, I could just toss the whole list into the local @ARGV, but this is efficient enough.

Thanks for the helpful suggestions on Tie::File. I'd probably go that way if doing this over. The configuration files I'm editing are never more than a few  KB in length, so a Tie wouldn't use too much memory.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

燕归巢 2024-11-15 16:44:31

File::Slurp 的最新版本提供了方便的功能,edit_fileedit_file_lines。代码的内部部分如下所示:

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

The recent versions of File::Slurp provide convenient functions, edit_file and edit_file_lines. The inner part of your code would look:

use File::Slurp qw(edit_file);
edit_file { s/$oldPass/$newPass/g } $filename;
原野 2024-11-15 16:44:31

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

The $^I variable only operates on the sequence of filenames held in $ARGV using the empty <> construction. Maybe something like this would work:

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

but if it's not a simple script and you need @ARGV and STDOUT for other purposes, you're probably better off using something like Tie::File for this task:

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;
}
迷荒 2024-11-15 16:44:31

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

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

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

Tie::File has already been mentioned, and is very simple. Avoiding the -i switch is probably a good idea for non-command-line scripts. If you're looking to avoid Tie::File, the standard solution is this:

  • Open a file for input
  • Open a temp file for output
  • Read a line from input file.
  • Modify the line in whatever way you like.
  • Write the new line out to your temp file.
  • Loop to next line, etc.
  • Close input and output files.
  • Rename input file to some backup name, such as appending .bak to filename.
  • Rename temporary output file to original input filename.

This is essentially what goes on behind the scenes with the -i.bak switch anyway, but with added flexibility.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文