读取整个文件然后在内部编辑时打印?

时间:2011-02-02 21:04:27

标签: perl inplace-editing

大多数就地编辑的例子都是单行,它们遍历一个或多个文件,一次读取和打印一行。

我找不到任何将整个文件读入数组,根据需要修改数组,然后在使用^ 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。

4 个答案:

答案 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';