Я пытаюсь найти пустые подкаталоги и удалить их. Я уверен, что есть лучшие способы добиться этого (я плохой программист и относительно новичок в Perl), но даже в этом случае я хотел бы понять, что не так с моим подходом.
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 );
Если в testdir есть пустой подкаталог с именем testsubdir, скажем, я получаю, казалось бы, противоречивый ответ:
Directory: /Users/username/testdir
Directory: /Users/username/testdir/testsubdir
Can't opendir(/Users/username/testdir/testsubdir): No such file or directory
Печать последнего каталога означает, что он прошел проверку -d, но последующее сообщение об ошибке говорит, что такого каталога нет. Насколько я вижу, между ними ничего не происходит.
Вы открыты для других решений, отличных от Perl? с находкой и ду.
См. также Безопасно ли переименовывать файлы при использовании readdir?
Код, так сказать, удаляет каталоги из-под ног find
.
Самое простое исправление: изменить find
на finddepth
для обратный обход, т.к.
it invokes the
&wanted
function for a directory after invoking it for the directory's contents.
(курсив автора) Тогда он не будет пытаться вызывать wanted
для только что удаленного каталога.
Или просто соберите список пустых каталогов в find
и удалите их после завершения find
.
Давайте добавим несколько операторов ведения журнала и посмотрим, что происходит:
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.
Так что код более-менее работает как задумано, просто File::Find
пытается ходить /Users/username/testdir/testsubdir
после того, как вы его удалили.
Я не вижу, как opendir когда-либо вызывался для testsubdir во второй раз после его удаления. Или вы говорите, что сообщение об ошибке opendir не из моего кода из внутренностей File::Find?
@JamesMachin Это действительно из «внутренностей» (когда вызывается wanted
), поскольку он пытается просмотреть список элементов (в то время как подкаталог только что был обработан и удален).
@zdim Понятно, спасибо за это разъяснение.
Использование du
и awk
.
Список всех пустых каталогов под $target_directory
du $target_directory| awk '$1= = "0"{print $2}'
Удалите все пустые каталоги под $target_directory
du $target_directory| awk '$1= = "0"{system("rmdir "$2);}'
Вы проверили разрешения? Ваш код не выводит сообщение об ошибке при сбое opendir, поэтому, вероятно, было бы полезно иметь именно тот код, который выдает точное сообщение об ошибке.