Hi folks. Today I had to write script that would delete all the empty directories in a directory tree recursively. Ie, removing any empty leaf dirs, and then moving up and removing the next layer if it is now empty, etc.

I thought that there must be a nice neat compact perlish way to do this, but it seemed to elude me. Anyway, the code I came up with is below, I'm wondering how compact this can be golfed down to by the folks out there that are good at golf. Requirements are to take a list of root dirs on the command line, delete any empty dirs, and then print out the number of dirs removed and the number kept. Ill accept anything that matches /D:\d+\s+F:\d+/ as the output string. It should die if there any errors removing the dirs, and the code need not worry about circular directories due to symlinks or the like. Have fun. :-) (You can assume there are no hidden or system files lurking in the tree, thanks davis for raising this issue.)

use strict; use warnings; use File::Find; use Cwd; my $deleted=0; my $keep=0; $|++; sub recurse { my $dir=shift; chdir $dir or die "chdir $dir:$!"; my $dirname=cwd; #print "$dirname\n"; my @dirs; my @files; for my $name (glob '*') { next unless $name=~/[^.]/; if (-d $name) { push @dirs,$name if recurse($name); } else { push @files,$name; } } chdir ".."; unless (@dirs or @files) { print "X: $dirname\n"; rmdir $dir or warn "$dirname:$!"; $deleted++; } else { print "k: $dirname (d: " . scalar(@dirs) . " | f: ".scalar(@files).")\n"; $keep++; } return @dirs+@files; } recurse $_ for @ARGV; print "Deleted $deleted directories, kept $keep directories\n";

---
demerphq

    First they ignore you, then they laugh at you, then they fight you, then you win.
    -- Gandhi


Replies are listed 'Best First'.
Re: (golf) Recursively delete empty directories from a tree
by Abigail-II (Bishop) on Feb 18, 2004 at 14:08 UTC
    This one doesn't count directories, and might break if the directories have funny characters in them. It doesn't use much Perl either, but it's waaaaaaaaaaaaaay shorter than yours:
    `find @ARGV -type d -depth | xargs rmdir`;
    If you modify it to:
    `find @ARGV -type d -depth | xargs rmdir 2>&1 | wc -l`;
    it'll tell you the amount of directories not removed.

    Abigail

      if you have the GNU text utilities most of them have a '-0' type option to seperate filenames with nulls.
      find . -type d -depth -print0 | xargs -0 prog
      which helps alot with the funky character problem. also there's the '-exec' option to find.
      find . -type d -depth -exec rmdir -- {} \;
      which (i think) bypasses the shell so funky characters are no problem. the '--' option is handy to turn off further argument processing so a directory like '-r' isn't taken as a flag.
Re: (golf) Recursively delete empty directories from a tree
by halley (Prior) on Feb 18, 2004 at 15:26 UTC
    Requirements are to take a list of root dirs on the command line, delete any empty dirs {...} and the code need not worry about circular directories due to symlinks or the like.

    It's often seen as bad form to invoke a lot of system commands which are likely to fail, but it seems to me that with these requirements, I could just rmdir() everything in sight, and if it failed to rmdir, then it was clearly not a leaf directory.

    @_=@ARGV; push@_,glob("$_/*")for@_; @_=grep{-d}reverse@_; $t=@_;rmdir$_&&$d++for@_; print"D:$d F:",$t-$d,$/;

    Update: added the requisite reporting, though still not trying very hard to reduce my handicap.

    I did notice that glob("$_/*") appeared to break on directory names with spaces in them, at least here at work across a Samba-supplied directory. I haven't looked into that further.

    --
    [ e d @ h a l l e y . c c ]

Re: (golf) Recursively delete empty directories from a tree
by borisz (Canon) on Feb 18, 2004 at 15:09 UTC
    Here is my first try:
    #!/usr/bin/perl use File::Find; my ( %h, $k, $d ); find( sub { $h{$File::Find::name} = 0 if -d $_; $h{$File::Find::dir}++ if -f _; }, $ARGV[0] ); chdir $ARGV[0]; !$h{$_} ? $d += rmdir: $k++ for ( sort { length($b) <=> length($a) } k +eys %h ); print "Deleted $d directories, kept $k directories\n";
    Boris
Re: (golf) Recursively delete empty directories from a tree
by zentara (Cardinal) on Feb 18, 2004 at 19:49 UTC
    How about this one? It could be even shorter if you remove the warning.
    #!/usr/bin/perl use File::Find; finddepth sub { return if $_ eq "." or $_ eq ".."; return unless -d; rmdir($_); if($! =~ /not empty/){warn "$File::Find::name $!"} }, @ARGV;
    UPDATE:
    #!/usr/bin/perl use File::Find; finddepth sub { return if (! -d) or ($_ eq '.'|'..'); rmdir($_)}, @ARGV;

    I'm not really a human, but I play one on earth. flash japh
      Your solutions don't meet demerphq's specification for printing out a tally of directories kept or removed... I made it shorter any-way :)
      use File::Find; finddepth sub {(!-d or /^.{1,2}$/)||rmdir($_)},@ARGV;
      I wonder what else a day that begins with me posting code to a golf thread will have in store *grin*

      --
      Do not seek to follow in the footsteps of the wise. Seek what they sought. -Basho

        finddepth sub {(!-d or /^.{1,2}$/)||rmdir($_)},@ARGV;
        didn't run for me, I needed to separate the rmdir with ;
        use File::Find; finddepth sub {(!-d or /^.{1,2}$/);rmdir},@ARGV;
        or with a print
        use File::Find; finddepth sub {(!-d or /^.{1,2}$/);rmdir; warn $File::Find::name,$! if $! =~ /not empty/},@ARGV;

        I'm not really a human, but I play one on earth. flash japh
Re: (golf) Recursively delete empty directories from a tree
by rinceWind (Monsignor) on Feb 18, 2004 at 16:27 UTC
    Sounds remarkably like the example in my recent post.

    Did you think about VMS's multiversioning I wonder?

    --
    I'm Not Just Another Perl Hacker

      You can't version a directory in VMS, I think. It's been a while, so correct me if I'm wrong. And since he's not interested in removing -f files, or directories which contain them, I think this is a bit different from any 1 while unlink() idiom.

      --
      [ e d @ h a l l e y . c c ]