Mery84 has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks.

Need help in create quite simple script to clean up recursively whole directory (cache dir) but need to add some exclusions.
This cache dir contains also some subdirs with name which begin from _ i.e. _05 Need to remove all dir, subdirs and content of those directories except directories with _ in name.
We have:
/cache/00/ /cache/01/ /cache/01/aa/ /cache/_01/ /cache/_01/aa/

It should remove /cache/00/ /cache/01/ but should left untouched /cache/_01/ and it subdirs.
It is possible to do it with rmtree?
#!/usr/bin/perl use warnings; use strict; use File::Find; use File::Path qw(make_path remove_tree); my $path="/opt/ssi/www/shop_templates/cache"; rmtree($path) or die "Cannot rmtree '$path' : $!";
It`ll rip off whole cache directory, it is possible to handle exclusions in rmtree?

Replies are listed 'Best First'.
Re: Rmtree with exclude
by jellisii2 (Hermit) on Dec 01, 2014 at 13:00 UTC
    File::Find in combination with File::Path is the route I would take here... it gives you a lot more flexibility. You can also restrict File::Find to just go down a specified depth to prevent a full recursion of all the subfolders.
    #!/usr/bin/perl use warnings; use strict; use File::Find; use File::Path qw(make_path remove_tree); my $path="/opt/ssi/www/shop_templates/cache"; find(\&audit, $path); sub audit { # your logic for which paths you want to keep/toss. # in here, the following apply: # $File::Find::dir = /opt/ssi/www/shop_templates/cache # $_ = foo.ext # $File::Find::name = /opt/ssi/www/shop_templates/cache/foo.ext ... }
Re: Rmtree with exclude
by Arunbear (Prior) on Dec 01, 2014 at 16:45 UTC
    rmtree doesn't handle such exclusions, however this task can also be achieved using some standard command line tools e.g.
    +4:35% find . -wholename '*/cache/*' ./cache/01 ./cache/01/aa ./cache/02 ./cache/02/aa ./cache/_01 ./cache/_01/aa +4:35% find . -wholename '*/cache/*' ! -wholename '*_*' ./cache/01 ./cache/01/aa ./cache/02 ./cache/02/aa +4:35% find . -wholename '*/cache/*' ! -wholename '*_*' | xargs rm -rf +4:36% find . -wholename '*/cache/*' ./cache/_01 ./cache/_01/aa
    See find, xargs
Re: Rmtree with exclude
by vinoth.ree (Monsignor) on Dec 01, 2014 at 14:49 UTC

    Hi,

    Here is my quick work around, there may be efficient available.

    use strict; use warnings; use Data::Dumper; use File::Find::Rule; use File::Path 'rmtree'; my $directory="/cache"; my @subdirs = File::Find::Rule->directory->in($directory ); print grep {!/$directory\/_.*?$/ and !/^$directory$/ and rmtree($_) } +@subdirs;

    All is well
Re: Rmtree with exclude
by james28909 (Deacon) on Dec 01, 2014 at 16:42 UTC
    use strict; use warnings; use File::Slurp; use File::Path qw( remove_tree ); use Cwd; my $dir = shift; #path/to/cache my @dirs = read_dir( $dir ); my $path = getcwd( $dir ); for (@dirs) { next if ( -f $_ ); if ( $_ !~ /(?:_)/ ) { my $removed = $path . "$dir/$_"; remove_tree($removed); print "removed : $removed\n"; } }
    I used Perl Regex Tester to create a regex that matches _ in the path, if found then it uses rmtree to remove it. This is completely untested as i am getting ready for work!

    EDIT: Changed =~ to !~ so it will match all dirs that dont have '_' in it.
    EDIT2: Just made the code so it should work as intended.
      Thank you James!
      Your code after few adjustments works like a charm.
      #!/usr/bin/perl use strict; use warnings; use File::Slurp; use File::Path qw( remove_tree ); my $dir = "/opt/ssi/cache"; my $err_list = "/home/az/log.txt"; my @dirs = read_dir( $dir ); for (@dirs) { next if ( -f $_ ); if ( $_ !~ /(?:_)/ ) { my $removed = "$dir/$_"; remove_tree( $removed , { verbose => 1, error => \my $err_list, }); print "removed : $removed\n"; } }
        Awesome man, glad i could help. Though i am sure any of the other examples would have did the trick as well ;)

        Also, you should be very careful with remove_tree, and if possible add in absolute path to folder that you want to delete, that way the path that gets fed to remove tree is the exact folder you want deleted.

        Upon revising this, I deleted a folder in the scripts dir that had the same name of a dir in the test dir (was my mistake for using the same folder name i guess). needless to say it had abunch of my perl scripts in it and now i am having to try to use recovery software to hopefully get them back.
Re: Rmtree with exclude
by Anonymous Monk on Dec 02, 2014 at 02:34 UTC
    It is usually best to search for the files first, building a list in-memory of the things to be removed, then to separately remove those items. File searches often get confused if the directory structure they're traversing changes beneath their feet.