需要 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;
}
问题是,这会将修改后的输出写入 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
File::Slurp
的最新版本提供了方便的功能,edit_file
和edit_file_lines
。代码的内部部分如下所示:The recent versions of
File::Slurp
provide convenient functions,edit_file
andedit_file_lines
. The inner part of your code would look:$^I
变量仅使用空<>
结构对$ARGV
中保存的文件名序列进行操作。也许这样的东西会起作用:但如果它不是一个简单的脚本,并且您需要
@ARGV
和STDOUT
出于其他目的,那么您最好使用类似Tie::File
对于此任务:The
$^I
variable only operates on the sequence of filenames held in$ARGV
using the empty<>
construction. Maybe something like this would work:but if it's not a simple script and you need
@ARGV
andSTDOUT
for other purposes, you're probably better off using something likeTie::File
for this task:Tie::File 已经提到过,非常简单。对于非命令行脚本来说,避免使用 -i 开关可能是一个好主意。如果您希望避免 Tie::File,标准解决方案是这样的:
无论如何,这基本上就是 -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:
This is essentially what goes on behind the scenes with the -i.bak switch anyway, but with added flexibility.