Прочитать весь файл, затем распечатать при редактировании на месте? - PullRequest
4 голосов
/ 03 февраля 2011

Большинство примеров редактирования на месте - это однострочные, которые перебирают файл или файлы, читая и печатая по одной строке за раз.

Я не могу найти никаких примеров чтения всего файла в массивизменив массив по мере необходимости, а затем напечатайте массив, используя переключатель ^ 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.

Ответы [ 4 ]

6 голосов
/ 03 февраля 2011

См. 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 = ();
    }
}
3 голосов
/ 03 февраля 2011
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 = ();
    }
}

Это чтение файла (ов) line-at-a, и в конце каждого файла (до его закрытия) выполняет манипуляцию.

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

Это читает все файлы за раз как отдельные записи.

2 голосов
/ 03 февраля 2011

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;
1 голос
/ 14 января 2013

Редактирование на месте 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';

...