Tie file not working for loops

2019-07-31 17:37发布

问题:

I have a script which pulls all the pm files in my directory and look for certain pattern and change them to desired value, i tried Tie::File but it's not looking to content of the 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;
 }

the output is

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

this is not working as intended(looking into the content of all pm file), if i try to do the same operation for some test file under my home for single file, the content is getting replaced

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

it’s working as intended

回答1:

Working fine for me:

#!/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;
}


回答2:

I would not recommend to use tie for that. This simple code below should do as asked

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: $!";
}

The glob from File::Glob allows you to specify filenames similarly as in the shell. See docs for accepted metacharacters. The :bsd_glob is better for treatment of spaces in filenames.

If you need to process files recursively then you indeed want a module. See File::Find::Rule

The rest of the code does what we must do when changing file content: copy the file. The loop reads each line, changes the ones that match, and writes each line to another file. If the match fails then s/ makes no changes to $line, so we just copy those that are unchanged.

In the end we move that file to overwrite the original using File::Copy.

The new file is temporary and I suggest to create it using File::Temp.


  The glob pattern "$dir/..." allows for an injection bug for directories with particular names. While this is very unusual it is safer to use the escape sequence

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

In this case File::Glob isn't needed since \Q escapes spaces as well.



回答3:

Solution using my favorite module: Path::Tiny. Unfortunately, it isn't a core module.

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
}