P Shoe
P Shoe

Reputation: 3

Need to loop through directory - delete lines that match pattern

Need to loop through a Unix directory and search each line in each file. If there is a pattern match delete the line. Was not able to get the line deletion to work so i'm just trying to find pattern and replace with another.

Populating an array with file names and looping through. I have a counter set it's looking at each of the lines in each file (at least they count is correct).

#!/usr/bin/perl -l
#!/usr/bin/perl -i.bak -w
#!/usr/bin/env perl

use strict;
use warnings;
use File::Find;

# 4-1-19
# pfs
# remove lines with dental code ADD2999 from all HMO Max load files in /home/hsxxx/dat/feeLoad directory

$| = 1;


chdir "/home/hstrn/dat/feeLoad";
chdir;


my $dir  = </home/hstrn/dat/feeLoad/>;
my @files;
my $count=0;

opendir(DIR, $dir) or die "Cannot open directory $dir, Perl says $!\n";

while (my $file = readdir DIR)
{
        push @files, "$dir/$file" unless -d "$dir/$file";
}

closedir DIR;

{
local @ARGV = @files;
while (<>)
{
        s/ADD2999/sometext/g;
        $count++;
}
print "Total lines read are: $count";

}

Would expect all strings ADD2999 to be replaced with sometext

Upvotes: 0

Views: 262

Answers (2)

ikegami
ikegami

Reputation: 385897

To remove lines, you need to avoid printing them when writing to the new file. Your code doesn't write to any files at all???


This might be a job for existing tools.

find /home/hstrn/dat/feeLoad -maxdepth 1 -type f \
   -exec perl -i~ -ne'print if !/ADD2999/' {} +

Use -i instead of -i~ if you want to avoid creating a backup. I prefer creating the backups, then deleting them once I've confirmed that everything is ok.

Show the files that are going to get deleted:

find /home/hstrn/dat/feeLoad -maxdepth 1 -type f -name '*~'

Delete the files:

find /home/hstrn/dat/feeLoad -maxdepth 1 -type f -name '*~' -delete

Upvotes: 2

Stefan Becker
Stefan Becker

Reputation: 5962

This would be my first attempt at the problem, but it could use some more corner case checking. E.g. how do you handle write-protected files, etc. It also assumes that the files are small enough to fit into memory for processing.

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

use File::Spec;
use File::Slurper qw(read_text write_text);

my $count = 0;
my $dir = "tmp";

opendir(my $dh, $dir);
while (readdir $dh) {
    # skip anything that shouldn't be processed
    next if /^\.\.?$/; # . && ..
    my $file = File::Spec->catfile($dir, $_);
    next if -d $file;  # directories

    # slurp file content and replace text
    my $content = read_text($file);
    my $matches = ($content =~ s/ADD2999/sometext/g);

    # count lines
    my @eols = ($content =~ /(\n)/g);
    $count += @eols;

    # replace original file if contents were modified
    write_text($file, $content) if $matches;
}
closedir($dh);

print "Total lines read are: $count\n";

exit 0;

Test run:

$ wc -l tmp/test*.txt
  5 tmp/test2.txt
  6 tmp/test.txt
 11 total
$ fgrep ADD2999 tmp/*.txt
tmp/test2.txt:asddsada ADD2999 asdsadasd
tmp/test2.txt:21312398 ADD2999 dasdas

$ perl dummy.pl
Total lines read are: 11

$ fgrep ADD2999 tmp/*.txt
$ fgrep sometext tmp/*.txt
tmp/test2.txt:asddsada sometext asdsadasd
tmp/test2.txt:21312398 sometext dasdas

If the files are large you will need to use line-by-line processing approach (just showing the contents of the loop). That has the side-effect that all files will be touched, although they might not have any replacements in it:

# read file and replace text
open(my $ifh, '<', $file);
my $tmpfile = File::Spec->catfile($dir, "$_.$$");
open(my $ofh, '>', $tmpfile);
while (<$ifh>) {
    s/ADD2999/sometext/g;
    print $ofh $_;
}
$count += $.; # total lines in $ifh
close($ofh);
close($ifh);

# replace original file with new file
unlink($file);
rename($tmpfile, $file);

Upvotes: 0

Related Questions