How can I use Perl, how can I find and delete files with a specific suffix?
I need to write a function in Perl that removes all files with a suffix .rc
in a specific location and its subdirectories (lets call it targetroot).
I work in NT env, so I cannot use system commands like find
or rm
. I got tired of doing it with tripping and finding options but failed.
I tried:
print "\n<<< Removing .rc files from $targetRoot\\20140929_231622 >>>\n";
my $dir = "$targetRoot\\20140929_231622";
find(\&wanted, $dir);
sub wanted
{
unlink glob "*.rc";
}
Can someone show me how to do this?
source to share
You're pretty close. File::Find
is a tool for working here. Try to put it wanted()
like this:
sub wanted {
# $_ set to filename; $File::Find::name set to full path.
if ( -f and m/\.rc\z/i ) {
print "Removing $File::Find::name\n";
unlink ( $File::Find::name );
}
}
Try it first without tripping, of course, to make sure you're getting the right goals. Remember that File::Find
will drop the directory structure by default.
source to share
You need to change the subroutine wanted
:
sub wanted { /\.rc$/ && ( unlink $File::Find::name or die "Unable to delete $_: $!" ) }
From File::Find
documentation
Function
wanted
The function
wanted()
performs whatever checks you want on each file and directory. Note that, despite its name, thewanted()
function is a generic callback function and does not tellFile::Find
if a file is "needed" or not. In fact, its return value is ignored.The required function takes no arguments, but does its job through a set of variables.
`$File::Find::dir` is the current directory name, `$_` is the current filename within that directory `$File::Find::name` is the complete pathname to the file.
source to share
Path :: Class makes it a little nicer:
#!/usr/bin/env perl
use strict;
use warnings;
use Path::Class;
run(@ARGV ? \@ARGV : ['.']);
sub run {
my $argv = shift;
my $dir = dir(shift @$argv)->resolve; # will croak if path does not exist
$dir->recurse(callback => \&rm_if_rcfile);
return;
}
sub rm_if_rcfile {
my $entity = shift;
return if $entity->is_dir;
return unless $entity =~ / [.] rc \z/ix;
print "$entity\n";
return; # remove this line to actually delete
unless ($entity->remove) {
warn "'$entity' failed: $!\n";
}
}
source to share
Using File::Find
:
use strict;
use warnings;
use autodie;
use File::Find;
my $dir = "targetRoot\\20140929_231622";
find(
sub {
unlink if -f && /\.rc$/i;
},
$dir
);
Or using File::Find::Rule
:
use strict;
use warnings;
use autodie;
use File::Find::Rule;
my $dir = "targetRoot\\20140929_231622";
for ( File::Find::Rule->file()->name('*.rc')->in($dir) ) {
unlink;
}
source to share
Try this block of code:
my $dir = "$targetRoot\\20140929_231622";
#subroutine call
wanted($dir);
#subroutine declaration
sub wanted
{
my $result = $_[0];
my @p = grep {-f} glob("$result\\*.rc");
foreach my $file (@p)
{
print "Removing files $file from the directory $dir" . unlink($file) . "\n";
}
}
source to share
Besides the definition $targetRoot
, all you have to do is change your wanted
sub.
File::Find
calls wanted
for every node (file or directory) it finds under the root directory you specify. It will put basename (the name of the bare file without path information) in $_
and execute chdir
in the directory that contains it.
Since the default action unlink
- if you don't pass a parameter - is to delete the file specified $_
, all you have to do is check that it is a file and ends with .rc
.
This version wanted
will do it for you.
sub wanted {
unlink if -f and /\.rc\z/i;
}
And this subroutine is so short that you can use an anonymous subroutine in the call find
. Here's the full version, which also warns that the deletion failed on any file.
use strict;
use warnings;
use File::Find;
my $targetRoot = 'C:\path\to\root';
my $dir = "$targetRoot\\20140929_231622";
print qq{\n<<< Removing .rc files from "$dir" >>>\n};
find(sub {
-f and /\.rc\z/i and unlink or warn qq{Unable to delete "$File::Find::name"\n};
}, $dir);
source to share