Perl 就地编辑产生垃圾

Perl in-place editing produces garbage

我在就地文件编辑方面遇到了问题,我浏览了几个小时的网页却没有结果。

我真的不想使用一般的临时文件方案,即将所有内容写入新文件并替换旧文件一。我需要修改时间戳以反映实际变化,权限和所有权保持不变等

如果我没理解错的话,使用 $I^ 只是临时文件方案的简写 - 还是我错了?

“+<”模式应该以读写方式打开文件。

我目前的测试代码:

#!/usr/bin/perl
use strict;
use warnings;

open(FILE, "+<", "testingfile") or die "$!";

while (<FILE>) {
    print;
    s/world/WORLD/;
    print FILE $_;
    print;
}

"testingfile"有三行,我暂时想把"world"换成"WORLD":

hello
world
foo

结果

当我 运行 Perl 脚本时,会产生垃圾并且终端会一直挂起直到被中断 (Ctrl+C):

hello
hello
foo
foo
o
o
llo
llo
ÈÈ'>jËNgs}>¾ØKeh%P8*   *        +       +      p+      ÑÑÀ+    +       p+      p+      ¨° #!/u8in/puse ct;
ÈÈ'>jËNgs}>¾ØKeh%P8*   *        +       +      p+      ÑÑÀ+    +       p+      p+      ¨° #!/u8in/puse ct;

"testingfile" 现在包含:

hello
world
foo
hello
hello
foo

我运行在 SunOS (Solaris) 生产系统上使用旧的 Perl:

This is perl, v5.8.4 built for i86pc-solaris-64int

最直接的方法是使用Tie::File,它允许您通过简单地修改数组来编辑文本文件。它确实有慢的名声,但您只有自己尝试一下才能知道它是否

您的示例代码将变成这样

#!/usr/bin/perl
use strict;
use warnings;

use Tie::File;

tie my @file, 'Tie::File', 'testingfile' or die $!;

s/world/WORLD/ for @file;

untie @file;
#!/usr/bin/perl
use strict;
use warnings;

# getlines
open(FILE, "<", "testingfile") or die "$!";
my @lines = <FILE>;
my $line = "";
close(FILE);

# open file again but this time for writing
open(FILE, ">", "testingfile") or die "$!";

# write adjusted text
foreach $line (@lines) {
    $line =~s/world/WORLD/;
    print FILE "$line";
    print "$line";
}

您需要了解 seek 命令以在文件中四处移动。您的文件句柄 FILE 有一个光标。从FILE读取后,它的光标指向刚刚读取的数据的末尾。然后你在 FILE 上写,你并没有覆盖你刚刚读取的数据,而是覆盖你正要读取的数据。

这是您的文件。第一次打开时,光标在文件开头。

 h e l l o \n w o r l d \n f o o \n EOF
^

接下来您使用 <FILE> 操作读取一行输入。将文本 "hello\n" 加载到变量 $_ 并移动 FILE 的光标:

 h e l l o \n w o r l d \n f o o \n EOF
             ^

接下来,您的替换失败并且不会更改 $_,并且您将 $_ 的内容打印到 FILE。书写从光标处开始,你得到

 h e l l o \n h e l l o \n f o o \n EOF
                          ^

下次读取时,在$_中得到foo\n,将光标移动到文件末尾,然后在文件末尾重写$_

 h e l l o \n h e l l o \n f o o \n f o o \n EOF
                                            ^

使用seek命令移动光标。也许像

open(FILE, "+<", "testingfile") or die "$!";

while (<FILE>) {
    print;
    if (s/world/WORLD/) {
        seek FILE, -length($_), 1;   # move back by length of $_
        print FILE $_;
    }
    print;
}

正如@Borodin 指出的那样,如果您想在文件中移动时延长或缩短 $_,这会变得更加复杂。

就地编辑不符合您的要求。它重命名原始文件,然后使用原始名称打开一个新文件。它从重命名的文件中读取并写入原始文件名。 -I的解释参见perlrun