perl - perl -i *really* 是如何实现的?

标签 perl stdin

在 Perl 的描述中 -i[extension]功能在 http://perldoc.perl.org/perlrun.html ,与以下程序实质上相同的代码被视为使用 perl -pi.orig ... 的“等效”。 :

#!/usr/bin/perl

use strict;
use warnings;

my $extension = '.orig';
my $oldargv = '';
my $backup;
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;
    }
    # Don't change anything; just copy.
}
continue {
    print;
}
select(STDOUT);

这在 $extension eq '.orig' 时工作正常;然而,Perl 定义了 -i也没有扩展名(即,对于 $extension eq '' )。 Perl 定义的行为是就地编辑文件,不创建备份文件:

If no extension is supplied, and your system supports it, the original file is kept open without a name while the output is redirected to a new file with the original filename. When perl exits, cleanly or not, the original file is unlinked.



也许我的系统(Mac OS X Yosemite 10.10.3)不支持它。

如果我设置 $extension = ''在这个程序中,那么对于小于一个 STDIN 块的文件(在 AcivePerl 5.10 中为 4096 字节,在 ActivePerl 5.16 中为 8192 字节),代码将可以正常工作,但它会 不是 适用于大于一个块的文件。

在我看来,在我的系统上,如果 $ARGV$backup具有相同的值(如果 $extension eq '' ,那么第 17 行的 open(ARGVOUT, ">$ARGV") 调用会在读取一个块后破坏输入文件。

当然,我可以通过写入临时文件然后在最后重命名来解决这个问题。但我有点失望,经过几个小时的调试,perlrun 中的示例并不像我预期的那样通用。
  • 是否有标准的、惯用的方法来处理 $extension eq ''案件?
  • 这是$extension eq ''用例足够重要以至于应该编辑 perlrun?当然,“并且您的系统支持它”子句意味着该示例没有错,但如果该示例也涵盖了这种情况,则会更有用。
  • 最佳答案

    Perl 5.28 changed -i .此答案适用于早期版本的 Perl。

    当提供扩展时:

    open(my $fh_in,  '<', $qfn);
    rename($qfn, "$qfn$ext");
    open(my $fh_out, '>', $qfn);
    

    这可以使用 strace 看到.
    $ strace perl -i~ -pe1 a
    ...
    open("a", O_RDONLY)                     = 3
    rename("a", "a~")                       = 0
    open("a", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4
    ...
    

    当没有提供扩展名时:
    open(my $fh_in,  '<', $qfn);
    unlink($qfn);
    open(my $fh_out, '>', $qfn);
    

    这可以使用 strace 看到.
    $ strace perl -i -pe1 a
    ...
    open("a", O_RDONLY)                     = 3
    unlink("a")                             = 0
    open("a", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4
    ...
    

    Mac 等 Unix 系统支持匿名文件。 Windows 没有,所以 -i需要在那里扩展。
    >perl -i.bak -pe1 a
    
    >perl -i -pe1 a
    Can't do inplace edit without backup.
    

    如果我们将这些知识整合到您发布的代码中,我们将得到以下信息:
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    my $extension = '.orig';
    my $oldargv = '';
    my $backup;
    LINE: while (<>) {
        if ($ARGV ne $oldargv) {
            if (length($extension)) {
                if ($extension !~ /\*/) {
                    $backup = $ARGV . $extension;
                } else {
                    ($backup = $extension) =~ s/\*/$ARGV/g;
                }
                rename($ARGV, $backup);
            } else {
                die("Can't do inplace edit without backup.\n") if $^O eq 'MSWin32';
                unlink($ARGV);
            }
            open(ARGVOUT, ">$ARGV");
            select(ARGVOUT);
            $oldargv = $ARGV;
        }
        # Don't change anything; just copy.
    }
    continue {
        print;
    }
    select(STDOUT);
    

    关于perl - perl -i *really* 是如何实现的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30538865/

    相关文章:

    Template Toolkit 中的 Perl 逻辑

    java - 用java重写Perl和shell脚本有意义吗?

    javascript - 如何将多行输入通过管道传递给 Node.js 程序?

    delphi - 如何使用 DELPHI 将文件写入 STDIN Stream?

    perl - 如何覆盖对模块函数的调用,然后在覆盖中引用原始函数?

    perl - 为什么Perl不支持双引号的哈希插值?

    mysql - 在 Perl 中将 MySQL 结果作为哈希表返回

    输出字符的 C 程序会生成整数

    bash - 将当前目录中的文件名列表作为单个字符串发送到 Docker 容器的 STDIN

    node.js - 如何为 Inquirer.js 编写单元测试?