在Perl脚本中使用perl one-liner

时间:2016-07-19 12:42:50

标签: perl in-place

我在一个perl脚本中嵌入了一个perl单行程序,其目的是为名称扩展名为.ini的文件的每一行就地替换另一个字符串,但我发现每个文件都已备份与.bak。

因为在单行中,我只指定-i而不是-i.bak,我想知道为什么可以生成.bak文件。

foreach my $dir (glob "*.ini") {
    system qq(perl -i -pe 's|Default|UTF-8|' $dir);          
}

另一个问题是,是否有更好,更简洁的perl脚本来实现相同的目标(没有要备份的.bak文件),无论是否有单行内容。

4 个答案:

答案 0 :(得分:2)

我想你可能正在查看一些旧的数据文件。据我所知,Perl在任何情况下都不会假设.bak的默认文件扩展名

如果您将选项指定为-i,那么它将尝试通过在打开输入文件后删除输入文件并创建具有相同名称的新输出文件来就地编辑文件。旧文件在关闭之前仍然可读

此技术在Windows上不起作用,因此在该平台上使用裸-i选项是错误的

没有必要为了编辑某些文件而进行第二个perl进程。它可以非常简单地完成。内部变量$^I对应于命令行上-i选项的值。再一次,如果您正在使用Windows系统,那么$^I可能不是空字符串,您需要将其设置为.bak或类似的东西

{
    local @ARGV = glob 'f.*';
    local $^I = '';  # Must be non-blank on Windows

    while ( <> ) {
        s/Default/UTF-8/g;
        print;
    }
}

答案 1 :(得分:1)

这是一种方法,无需shell-out另一个perl实例。此技术使用中间临时文件:

use warnings;
use strict;

for my $file (glob "*.ini") {
    open my $fh, '<', $file or die $!;
    open my $wfh, '>', 'temp.txt' or die $!;

    while (<$fh>){
        s/Default/UTF-8/;
        print $wfh $_;
    }
    close $fh;
    close $wfh;
    unlink $file or die $!;
    rename 'temp.txt', $file;
}

如果您知道文件不是太大,请使用此版本。它将整个文件读入内存(单个字符串标量),并且不需要中间临时文件:

use warnings;
use strict;

for my $file (glob "*.ini") {
    open my $fh, '<', $file or die $!;

    my $contents;
    {
        local $/;
        $contents = <$fh>;
    }
    close $fh;

    $contents =~ s/Default/UTF-8/;

    open my $wfh, '>', $file or die $!;
    print $wfh $contents;
    close $wfh;
}

答案 2 :(得分:1)

perl -i归结为以下核心:

open(my $fh_in, '<', $qfn);
unlink($qfn);
open(my $fh_out, '>', $qfn);

$fh_in被称为匿名文件句柄。请注意目录中不再存在它所读取的关联文件。

问题是Windows不支持匿名文件句柄。删除已打开的文件时,目录条目将一直保留,直到文件关闭。这可以防止Perl创建具有相同名称的输出文件。这就是Windows上不支持-i(没有扩展名)的原因。

>perl -i -ne"..." file
Can't do inplace edit without backup.

但你没有使用Windows。您正在使用cygwin,一个模拟unix的环境。这就是为什么使用-i(没有扩展名)并没有给你一个错误。但是,cygwin仍然局限于主机操作系统的功能,因此无法创建匿名文件 [1] 。因此,当将-i.bak提供给Perl的cygwin构建时,Perl被编程为表现为-i的行为。

  1. 似乎能够模仿它们,但由于某种原因,该功能并未被使用。

    $ cat foo
    abc
    def
    
    $ perl -E'
       open(my $fh, "<", "foo") or die $!;
       system("ls -1");
       say "--";
       unlink("foo") or warn $!;
       system("ls -1");
       say "--";
       print while <$fh>;
    '
    foo
    --
    --
    abc
    def
    

答案 3 :(得分:0)

我认为bash命令find + xargs和perl应该比perl循环更容易。

find . -name '*.ini' |xargs perl -pi -e 's/Default/UTF-8/g'