Reputation: 179
I'm trying to find empty subdirectories and delete them. I'm sure there are better ways to achieve this (I'm a poor programmer and relatively new with Perl) but even so, I'd like to understand what's wrong with my approach.
use strict;
use warnings;
use File::Basename;
use File::Find
my $lambda2 = sub
{
my $path = $File::Find::name;
if ( -d $path )
{
print("Directory: ", $path, "\n");
# Define anonymous function to test if directory is empty
my $hasContent = sub
{
my $directory = shift;
opendir ( my $dh, $directory );
return scalar ( grep { $_ ne "." && $_ ne ".." } readdir ( $dh ) );
};
# Remove item if it is an empty directory
if ( ! $hasContent->( $path ) )
{
rmdir( $path );
}
}
};
my $directory = "/Users/username/testdir/";
find( { wanted => $lambda2, no_chdir => 1 }, $directory );
If testdir has an empty subdirectory called testsubdir, say, I get the seemingly contradictory response:
Directory: /Users/username/testdir
Directory: /Users/username/testdir/testsubdir
Can't opendir(/Users/username/testdir/testsubdir): No such file or directory
The printing of the latter directory implies that it passed the -d check, but the subsequent error message says there is no such directory. As far as I can see nothing occurs inbetween.
Upvotes: 3
Views: 197
Reputation: 66883
The code's removing directories under find
's feet, so to speak.
The simplest fix: change find
to finddepth
, for postorder traversal, since
it invokes the
&wanted
function for a directory after invoking it for the directory's contents.
(original emphasis) Then it won't attempt to invoke wanted
on the directory just removed.
Or, merely collect the list of empty directories in find
and delete them after find
completes.
Upvotes: 5
Reputation: 4865
Using du
and awk
.
List all empty directories under $target_directory
du $target_directory| awk '$1=="0"{print $2}'
Remove all empty directories under $target_directory
du $target_directory| awk '$1=="0"{system("rmdir "$2);}'
Upvotes: 0
Reputation: 118605
Let's throw in some logging statements and see what is happening:
my $lambda2 = sub {
my $path = $File::Find::name;
if ( -d $path ) {
print("Directory: ", $path, "\n");
my $hasContent = sub {
my $directory = shift;
opendir ( my $dh, $directory );
return scalar ( grep { $_ ne "." && $_ ne ".." } readdir ( $dh ) );
};
my $hc = $hasContent->($path);
print STDERR "hc($path) = $hc\n";
if (! $hc) {
print STDERR "Deleting $path\n";
rmdir( $path );
}
}
};
$ mkdir -p /Users/username/testdir/testsubdir
$ perl subdir.pl
Directory: /Users/username/testdir
hc(/Users/username/testdir) = 1
Directory: /Users/username/testdir/testsubdir
hc(/Users/username/testdir/testsubdir) = 0
Deleting /Users/username/testdir/testsubdir
Can't opendir(/Users/username/testdir/testsubdir): No such file or directory
at subdir.pl line 26.
So the code is more or less working as designed, it's just that File::Find
is trying to walk /Users/username/testdir/testsubdir
after you have deleted it.
Upvotes: 4