就地编辑时读取整个文件然后打印?

发布于 2024-10-15 21:38:13 字数 637 浏览 3 评论 0原文

就地编辑的大多数示例都是单行代码,它循环访问一个或多个文件,一次读取并打印一行。

我找不到任何将整个文件读入数组、根据需要修改数组,然后在使用 ^I 开关进行就地编辑时打印数组的示例。当我尝试从 Diamond 操作符读取整个文件、编辑内容并打印整个内容时,我发现打印转到 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。

Most examples of inplace editing are one-liners that iterate through a file or files, reading and printing one line at a time.

I can't find any examples of reading an entire file into an array, modifying the array as needed, and then printing the array while using the ^I switch to do an inplace edit. When I try to read the entire file from the diamond operator, edit the contents and print the entire contents, I find that the print goes to STDOUT instead of ARGVOUT and that ARGVOUT is closed. I can open the same file for output and then print to it, but I'm not sure I understand why that is necessary. Here is an example:

#!/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?

Running the above makes a backup of the test.txt file as expected, but leaves the edited test.txt empty, printing the edited contents to STDOUT instead.

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

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

发布评论

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

评论(4

伊面 2024-10-22 21:38:13

请参阅 perlrun

-i 开关被调用时,perl 使用 ARGVOUT 作为默认文件句柄而不是 STDOUT 来启动程序。如果有多个输入文件,则每次 <>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 = ();
    }
}

See perlrun.

When the -i switch has been invoked, perl starts the program using ARGVOUT as the default file handle instead of STDOUT. If there are multiple input files, then every time the <> or <ARGV> or readline(ARGV) operation finishes with one of the input files, it closes ARGVOUT and reopens it to write to the next output file name.

Once all the input from <> is exhausted (when there are no more files to process), perl closes ARGVOUT and restores STDOUT as the default file handle again. Or as perlrun says

#!/usr/bin/perl -pi.orig
s/foo/bar/;

is equivalent to

#!/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);

Once you say my @file = <> and consume all the input, Perl closes the filehandle to the backup files and starts directing output to STDOUT again.


The workaround, I think, is to call <> in scalar context and check eof(ARGV) after each line. When eof(ARGV)=1, you have read the last line in that file and you get one chance to print before you call <> again:

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 = ();
    }
}
壹場煙雨 2024-10-22 21:38:13
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;
}

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

my @file = <>; #Read all records into array

is bad. Now you're done slurping all the records, *ARGV is closed, and $^I replacement doesn't have anything to work on.

my @file;
while (<>) {
    push @file, $_;
}
continue {
    if (eof ARGV) {
        chomp @file;
        push @file, qw(add a few more lines);
        print join "\n", @file;
        @file = ();
    }
}

This read the file(s) line-at-a-time, and at the end of each file (before it's closed), performs the manipulation.

undef $/;
while (<>) {
    my @file = split /\n/, $_, -1;
    push @file, qw(add a few more lines);
    print join "\n", @file;
}

This reads entire files at a time as single records.

违心° 2024-10-22 21:38:13

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;

Tie::File can also be used to edit a file in-place. It does not leave a backup copy of the original file, however.

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;
最舍不得你 2024-10-22 21:38:13

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

Perl's inplace editing is much simpler than any of the answers:

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

if you want to create a backup then change local $^I = ''; to local $^I = '.bak';

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