4

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

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

4 回答 4

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 = ();
    }
}
于 2011-02-02T21:23:13.277 回答
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;
}

这一次读取整个文件作为单个记录。

于 2011-02-02T21:28:19.880 回答
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;
于 2011-02-02T21:24:48.113 回答
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';

于 2013-01-14T16:39:40.373 回答