Как удалить каталог и его пустых предков с помощью Perl?
Как Perl удаляет каталог, а затем все пустые родительские каталоги до первого непустого? Другими словами, что можно использовать вместо:
system 'rmdir', '-p', $directory;
который, начиная с d
, сначала удалили d
а потом c
, а потом b
, но нет a
, поскольку a
будет по-прежнему содержать x
, как это:
a
a/b
a/b/c
a/b/c/d
a/x
в результате чего
a
a/x
Это не встроенный rmdir
, так как он может удалить только один каталог. ( док)
Это не finddepth ( sub {rmdir}, '.' )
, с помощью File::Find
как убирает детей, а не родителей. ( док)
Это не remove_tree
функция File::Path
Модуль также, так как он не только удаляет дочерние каталоги, но и файлы. ( док)
Обратите внимание, remove_tree
а также finddepth
работать в обратном направлении Баш rmdir --parent
,
3 ответа
use Path::Tiny qw( path );
my $p = path('a/b/c/d');
while (!$p->is_rootdir()) {
if (!rmdir($p)) {
last if $!{ENOTEMPTY};
die("Can't remove \"$p\": $!\n");
}
$p = $p->parent;
}
Заметки:
Эффективное. Проверяя результат
rmdir
Вместо того, чтобы использовать->children
или же->iterator
Это решение позволяет избежать ненужных звонковreaddir
,Нет гоночных условий. В отличие от решений, которые используют
readdir
(с помощью->children
или же->iterator
), это решение не страдает от состояния гонки.Это решение также позволяет избежать лишних
-d
проверьте, использованный ранее решением.Это решение, в отличие от предыдущих, будет обрабатывать пустое дерево, за исключением каталогов, которые нужно удалить.
AFAIK это не существует. Вы можете написать это довольно легко с помощью Path:: Tiny. Это простая рекурсивная функция.
#!/usr/bin/env perl
use strict;
use warnings;
use v5.10;
use Carp;
use Path::Tiny;
use autodie;
sub Path::Tiny::rmdir_if_empty {
my $self = shift;
# Stop when we reach the parent.
# You can't rmdir('.') anyway.
return if $self eq '.';
croak "$self is not a directory" if !$self->is_dir;
# Stop if the directory contains anything.
# I use an iterator to avoid a possibly very long list.
my $iter = $self->iterator;
return if defined $iter->();
# rmdir will not delete a non-empty directory, a second safeguard
rmdir $self;
return $self->parent->rmdir_if_empty;
}
path("a/b/c/d")->rmdir_if_empty;
Как всегда, используя мой любимый путь:: Tiny
use 5.014;
use warnings;
use Path::Tiny;
use autodie;
my $p = path('a/b/c/d'); # starting
die "$p is not a dir" unless -d $p;
while( ! $p->children ) { # if it is empty
rmdir $p; # remove it
$p = $p->parent; # go upward
}