4

インプレース編集のほとんどの例は、1つまたは複数のファイルを反復処理し、一度に1行ずつ読み取りおよび印刷するワンライナーです。

ファイル全体を配列に読み込み、必要に応じて配列を変更し、^Iスイッチを使用してインプレース編集を実行しながら配列を印刷する例は見つかりません。ダイヤモンド演算子からファイル全体を読み取り、内容を編集して内容全体を印刷しようとすると、印刷がARGVOUTではなくSTDOUTに送られ、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<>複数の入力ファイルがある場合、 or<ARGV>または操作が入力ファイルの 1 つを終了するたびに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);

すべての入力を読み込んで消費すると、Perlはmy @file = <>バックアップ ファイルへのファイル ハンドルを閉じ、出力をSTDOUT再度送信し始めます。


回避策は、スカラーコンテキストで呼び出し、各行の後<>にチェックすることだと思います。eof(ARGV)の場合、そのファイルの最後の行を読み、もう一度eof(ARGV)=1呼び出す前に印刷する機会が 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 = ();
    }
}

これにより、ファイルが一度に 1 行ずつ読み取られ、各ファイルの最後 (ファイルが閉じられる前) で操作が実行されます。

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 に答える