大多数就地编辑的例子都是单行,它们遍历一个或多个文件,一次读取和打印一行。
我找不到任何将整个文件读入数组,根据需要修改数组,然后在使用^ I开关进行就地编辑时打印数组的示例。当我尝试从钻石操作员读取整个文件,编辑内容并打印整个内容时,我发现打印转到STDOUT而不是ARGVOUT并且ARGVOUT已关闭。我可以打开相同的文件输出然后打印到它,但我不确定我理解为什么这是必要的。这是一个例子:
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
my $filename = 'test.txt';
push @ARGV, $filename;
$^I = ".bk";
my @file = <>; #Read all records into array
chomp @file;
push @file, qw(add a few more lines);
print join "\n", @file; #This prints to STDOUT, and ARGVOUT is closed. Why?
运行上述操作可以按预期方式备份test.txt文件,但将编辑后的test.txt保留为空,将编辑后的内容打印到STDOUT。
答案 0 :(得分:6)
请参阅perlrun
。
当调用-i
开关时,perl使用ARGVOUT
作为默认文件句柄而不是STDOUT
来启动程序。如果有多个输入文件,则每次<>
或<ARGV>
或readline(ARGV)
操作完成其中一个输入文件时,它将关闭ARGVOUT
并重新打开以写入下一个输出文件名。
一旦来自<>
的所有输入都已用完(当没有更多文件需要处理时),perl将关闭ARGVOUT
并再次将STDOUT
恢复为默认文件句柄。或者perlrun
说
#!/usr/bin/perl -pi.orig
s/foo/bar/;
相当于
#!/usr/bin/perl
$extension = '.orig';
LINE: while (<>) {
if ($ARGV ne $oldargv) {
if ($extension !~ /\*/) {
$backup = $ARGV . $extension;
}
else {
($backup = $extension) =~ s/\*/$ARGV/g;
}
rename($ARGV, $backup);
open(ARGVOUT, ">$ARGV");
select(ARGVOUT);
$oldargv = $ARGV;
}
s/foo/bar/;
}
continue {
print; # this prints to original filename
}
select(STDOUT);
一旦你说my @file = <>
并消耗了所有输入,Perl会关闭文件句柄到备份文件并开始再次将输出定向到STDOUT
。
<小时/>
我认为,解决方法是在标量上下文中调用<>
并在每行后检查eof(ARGV)
。当eof(ARGV)=1
时,您已阅读该文件中的最后一行,并且在再次致电<>
之前您有一次机会打印:
my @file = ();
while (<>) {
push @file, $_;
if (eof(ARGV)) {
# done reading current file
@processed_file = &do_something_with(@file);
# last chance to print before ARGVOUT gets reset
print @processed_file;
@file = ();
}
}
答案 1 :(得分:3)
my @file = <>; #Read all records into array
很糟糕。现在你已经完成了所有记录的填充,*ARGV
已关闭,$^I
替换没有任何可用的工作。
my @file;
while (<>) {
push @file, $_;
}
continue {
if (eof ARGV) {
chomp @file;
push @file, qw(add a few more lines);
print join "\n", @file;
@file = ();
}
}
这一次读取文件,并在每个文件的末尾(在它关闭之前)执行操作。
undef $/;
while (<>) {
my @file = split /\n/, $_, -1;
push @file, qw(add a few more lines);
print join "\n", @file;
}
这样可以将整个文件作为单个记录一次读取。
答案 2 :(得分:2)
Tie::File也可用于就地编辑文件。但是,它不会留下原始文件的备份副本。
use warnings;
use strict;
use Tie::File;
my $filename = 'test.txt';
tie my @lines, 'Tie::File', $filename or die $!;
push @lines, qw(add a few more lines);
untie @lines;
答案 3 :(得分:1)
Perl的就地编辑比任何答案都简单得多:
sub edit_in_place
{
my $file = shift;
my $code = shift;
{
local @ARGV = ($file);
local $^I = '';
while (<>) {
&$code;
}
}
}
edit_in_place $file, sub {
s/search/replace/;
print;
};
如果您要创建备份,请将local $^I = '';
更改为local $^I = '.bak';