【问题标题】:Tie file not working for loops领带文件不适用于循环
【发布时间】:2017-03-03 07:21:09
【问题描述】:

我有一个脚本可以提取我目录中的所有 pm 文件并查找某些模式并将它们更改为所需的值,我尝试了 Tie::File 但它不查找文件的内容

use File::Find;
use Data::Dumper qw(Dumper);
use Tie::File;
my @content;
find( \&wanted, '/home/idiotonperl/project/');

sub wanted {
    push @content, $File::Find::name;
return;  
}
my @content1 = grep{$_ =~ /.*.pm/} @content;
@content = @content1;
for my $absolute_path (@content) {
    my @array='';
    print $absolute_path;
    tie @array, 'Tie::File', $absolute_path or die qq{Not working};
    print Dumper @array;
    foreach my $line(@array) {
         $line=~s/PERL/perl/g;
    }
    untie @array;
 }

输出是

 Not working at tiereplacer.pl line 22.
 /home/idiotonperl/main/content.pm

这没有按预期工作(查看所有 pm 文件的内容),如果我尝试对我家下的某些测试文件执行相同的操作以获取单个文件,则内容将被替换

  @content = ‘home/idiotonperl/option.pm’

它按预期工作

【问题讨论】:

  • 你坚持使用tie——你有什么(非常)特别的原因吗?
  • 否...发现这种方式很容易编辑文件中的特定行
  • 我会推荐使用tie,除非你有非常充分的理由。我以相当标准的方式发布了如何执行您所要求的操作。告诉我进展如何。
  • @zdim:我非常不同意。 Tie::File 可以让“简单的事情变得简单”。如果tie 施加的开销在几毫秒或更短的数量级,那么担心它是没有意义的。 (我想那是你的反对意见?你不解释。)
  • @Borodin 我并不是要讨论它的价值。这就像一个魔法。我的意思是说它不应该用于只用最基本的工具就能轻松完成的工作。它只是提高了那里的复杂性。如果 OP 使用了一个简单的循环,这个问题就不会发生。 (我应该解释一下我的意思。)我并不是要抱怨开销。正确评估是使用任何工具的一部分。

标签: perl perl-module


【解决方案1】:

我不建议为此使用tie。下面这个简单的代码应该按照要求做

use warnings;
use strict;
use File::Copy qw(move);    
use File::Glob ':bsd_glob';

my $dir = '/home/...';

my @pm_files = grep { -f } glob "$dir/*.pm";

foreach my $file (@pm_files) 
{
    my $outfile = 'new_' . $file;  # but better use File::Temp

    open my $fh,     '<', $file    or die "Can't open $file: $!";
    open my $fh_out, '>', $outfile or die "Can't open $outfile: $!";

    while (my $line = <$fh>)
    {
        $line =~ s/PERL/perl/g;
        print $fh_out $line;     # write out the line, changed or not
    }
    close $fh;
    close $fh_out;

    # Uncomment after testing, to actually overwrite the original file
    #move $outfile, $file  or die "Can't move $outfile to $file: $!";
}

File::Glob 中的 glob 允许您像在 shell 中一样指定文件名。请参阅文档了解已接受的元字符:bsd_glob 更适合处理文件名中的空格。

如果您需要递归处理文件,那么您确实需要一个模块。见File::Find::Rule

剩下的代码做了我们在改变文件内容时必须做的事情:复制文件。循环读取每一行,更改匹配的行,并将每一行写入另一个文件。如果匹配失败,则s/ 不会对$line 进行任何更改,因此我们只需复制未更改的内容。

最后我们move那个文件用File::Copy覆盖原来的。

新文件是临时文件,我建议使用File::Temp 创建它。


glob 模式 "$dir/..." 允许对具有特定名称的目录进行注入错误。虽然这很不寻常,但使用escape sequence 会更安全

my @pm_files = grep { -f } glob "\Q$dir\E/*.pm";

在这种情况下,File::Glob 不需要,因为 \Q 也会转义空格。

【讨论】:

  • 也许,只过滤文件:grep -f, glob "$dir/*.pm";
  • $file 仍处于打开状态时,我不确定你能否做到这一点move
  • @Borodin 哦,是的。非常感谢你。那会很糟糕。
【解决方案2】:

使用我最喜欢的模块的解决方案:Path::Tiny。不幸的是,它不是核心模块。

use strict;
use warnings;
use Path::Tiny;

my $iter = path('/some/path')->iterator({recurse => 1});
while( my $p = $iter->() ) {
        next unless $p->is_file && $p =~ /\.pm\z/i;
        $p->edit_lines(sub {
            s/PERL/perl/;
            #add more line-editing
        });
        #also check the path(...)->edit(...) as an alternative
}

【讨论】:

  • 同意,包括“不幸”:)
【解决方案3】:

对我来说工作得很好:

#!/usr/bin/env perl
use common::sense;
use File::Find;
use Tie::File;

my @content;

find(\&wanted, '/home/mishkin/test/t/');

sub wanted {
    push @content, $File::Find::name;
    return;
}

@content = grep{$_ =~ /.*\.pm$/} @content;

for my $absolute_path (@content) {
    my @array='';
    say $absolute_path;
    tie @array, 'Tie::File', $absolute_path or die "Not working: $!";

    for my $line (@array) {
        $line =~ s/PERL/perl/g;
    }

    untie @array;
}

【讨论】:

  • 对我来说很好...在写入文件时遇到了一些权限问题
  • my @array = () 不是更好吗?
  • 另外,您不需要@content = grep{$_ =~ /.*\.pm$/} @content;,因为您可以直接在wanted 函数中过滤文件名,例如:find( sub{ push @content, $File::Find::name if -f &amp;&amp; /\.pm$/i; }, q{.} );
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-05
  • 2014-01-29
  • 2017-01-26
  • 2014-09-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多