http://www.perlmonks.org?node_id=37326

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

Well, here's a little script request that I'm sure some of you, my friends, have already come across at some point...

I have a directory tree, containing MANY files and MANY subdirectories, and I would like to be able to delete directories matching a specified pattern along with all the files (and maybe subdirectories) that they contain... something that the poor DEL command is far from being able to do!

Thanks for a quick help.
  • Comment on Recursive Directory Tree Deletion in Windows

Replies are listed 'Best First'.
RE: Recursive Directory Tree Deletion in Windows
by cwest (Friar) on Oct 18, 2000 at 18:34 UTC
RE: Recursive Directory Tree Deletion in Windows
by arturo (Vicar) on Oct 18, 2000 at 18:12 UTC

    Probably what you want is File::Find, which will recurse subdirectories for you and you can specify what to do with each file. Read up on it around this site, there are a number of threads concerning how to use it.

    Philosophy can be made out of anything -- or less

      Well, File::Find should help you out. But if not, do a SuperSearch on File::Find.
Re: Recursive Directory Tree Deletion in Windows
by djw (Vicar) on Oct 18, 2000 at 20:22 UTC
    Like the others said use File::Find::finddepth to recursively toast directories (obviously be very careful with this as it acts exactly like rm -r). If you have the perl cookbook there is an explination on page 325 of this exact thing.

    If you don't have that then take a look at the code in the File:Find module (win32: %perl_install_dir%\lib\File\Find.pm ).

    Also remember that the 'rmdir' command won't delete directories if they have files and/or subdirectories still in them.


    Thanks,
    djw
Re: Recursive Directory Tree Deletion in Windows
by OzzyOsbourne (Chaplain) on Oct 18, 2000 at 22:12 UTC
    You can pick out what you need...
    # ********************************* # Call Modules # ********************************* use File::Find; use File::Copy; use Getopt::Std; getopts('d:h'); # ********************************* # Initialize variables # ********************************* $server='server104'; $dir1='\\\\'."$server".'\\d$'; $agedays=60; #defaults agedays to 60 # ********************************* # Process arguments ([h]elp,[d]ays) # ********************************* if ($opt_h){ die "\agecmd.pl -d[days to age]\n"; } if ($opt_d){ if ($opt_d gt 9){ #tests for strings die "Error: days option must be a number. Use agecmd.pl -d[da +ys to age]\n"; }else{ $agedays=$opt_d; } } # ********************************* # Do date calculations # ********************************* @xtime=localtime(time); $day=$xtime[3]+1; $month=$xtime[4]+1; $year=$xtime[5]+1900; $hours=$xtime[2]; $mins=$xtime[1]; $secs=$xtime[0]; $logname='//server1/d$/'."ftp$month$day$year$hours$mins$secs".'.log'; $agesecs=60*60*24*$agedays; #converts $agedays to seconds $daysago=time-$agesecs; #the time stamp of $agedays ago in seco +nds $daysago2=localtime($daysago); #the time stamp of $agedays ago in w +ords - mainly for printing # ********************************* # Find the files (no dir) on the server # ********************************* print "finding files to be aged\.\.\.\n"; find(\&wanted, $dir1); sub wanted { $filesecs = (stat("$File::Find::dir/$_"))[9]; #GETS THE 9TH ELEMEN +T OF file STAT - THE MODIFIED TIME $filesecs2=localtime($filesecs); if ($filesecs<$daysago && -f){ #-f=regular files, eliminates DIR p +.367 push (@files,"$File::Find::dir/$_"); push (@files,"$filesecs2"); } print'.'; } # ********************************* # replace '/' with '\' in file names # ********************************* foreach (@files){ s/\//\\/g; } # ********************************* # Write to Log # ********************************* %filehash=@files; #puts the array into a hash for easy printing. Key= +file, Value=date open OUT, ">$logname" or die "Cannot open $out for write :$!"; print OUT "FTP server aging log generated by PERL script\n"; print OUT "Script written by Ozzy on 4/18/00\n"; print OUT "Files deleted from \\\\$server on ".localtime(time)."\n"; print OUT "Files deleted before $daysago2\n"; print OUT "Files were aged $agedays days\n\n"; print OUT "File Listing:\n\n"; foreach $filename (sort keys %filehash){ print OUT "$filename $filehash{$filename}\n"; } close OUT; # ********************************* # Delete files # ********************************* print "\nDeleting all files before $daysago2"; #unlink %filehash; print "\nScript complete.";

    -OzzyOsbourne

Re: Recursive Directory Tree Deletion in Windows
by the_slycer (Chaplain) on Oct 18, 2000 at 21:39 UTC
    As it happens I was working on something similar to this last nite. I spent a couple of hours on it before I discovered File::Find - I really have to read more, might be able to stop duplicating effort. Anyhow, here is the code that I wrote, someone let me know what problems there are.. other than the obvious dup of effort :-).
    use strict; unless ($ARGV[0]){ print "Please enter a string to look for: "; $ARGV[0]=<STDIN>; chomp @ARGV; } my $delkey = $ARGV[0]; my $startdir="c:/blah"; my (@dirs, @newdirs, @dellist); print "searching $startdir\n"; #build a list of files/dirs: my @list = glob("$startdir*"); #run through the list, if a dir, goes to @dir array (with a / at the e +nd) foreach (@list){ push (@dirs => "$_/") if -d; if (/.*$delkey*/oi){ push (@dellist => "$_/") if -d; } } #call the subroutine - this does the same thing on the next set of dir +s down build(@dirs); #if there are further subdirs, call the sub again while (@newdirs){ build($newdirs[0]); shift(@newdirs); } sub build{ my @dirs=@_; while (@dirs){ my $dir = shift (@dirs); print "searching $dir\n"; my @list = glob("$dir*"); foreach (@list){ push (@newdirs => "$_/") if -d; if (/.*$delkey*/oi){ push (@dellist => "$_/") if -d; } } } } foreach (reverse @dellist){ print "found: $_ to delete\n"; chdir "$_" || die "Unable to change to $_ : $!"; unlink (<*>); chdir "$startdir" || die "Unable to change back to $startdir : $!" +; rmdir "$_" || die "Unable to rmdir on $_ : $!"; }
RE: Recursive Directory Tree Deletion in Windows
by gaggio (Friar) on Oct 18, 2000 at 20:30 UTC
    I am so mean, that I even give the script away now.
    I think that you really should keep voting me down! (there are always going to be things that I won't understand in the world, even in nice places like PM:-( )

    Note: the script might have to be run several times, because it does not delete directories when they still have files in them. - Just keep running it until 0 file and 0 directory are deleted.

    #!/usr/local/bin/perl -w use File::Find; $workingdir = 'C:/TEMP/'; $countdirs = 0; $countfiles = 0; $testmode = 0; @extensions = ('txt','obj'); #files to remove @directories = ('win32-VC60','CVS','Cvs'); #directories to remove (rec +ursively with all the files/subs in them) @anymatches = ('ChangeLog'); #other special matches to remove as well find(\&processmatch, $workingdir); print "\n$countfiles file(s) and $countdirs director(y)(ies) were dele +ted.\n\n"; sub processmatch{ # Note: we are chdir in the current directory! $saved = $_; $name = $File::Find::name; $deleteflag = 0; foreach $ext(@extensions) { if($name =~ /.*\.$ext/) { $deleteflag = 1; } } foreach $dir(@directories) { if($name =~ /.*\/$dir.*/) { $deleteflag = 1; } } foreach $any(@anymatches) { if($name =~ /.*$any.*/) { $deleteflag = 1; } } if($deleteflag == 1) { if (-d $name) { if(!$testmode) { rmdir $name; } print "$name\n"; $countdirs++; } else { if(!$testmode) { unlink $saved; } print "$saved\n"; $countfiles++; } } $_ = $saved; }

    BTW: Thanks for the persons who helped me!
      the script might have to be run several times, because it does not delete directories when they still have files in them. - Just keep running it until 0 file and 0 directory are deleted.

      Use finddepth() instead of find() to have File::Find report all of a directory's entries before reporting the directory name itself. Depth-first searching allows you do clean out a directory before you try to delete it.

Re: Recursive Directory Tree Deletion in Windows
by Anonymous Monk on Oct 20, 2000 at 03:50 UTC
    Am I missing something, or is the underlying OS command relevant here?
    C:\WINDOWS>deltree -? Deletes a directory and all the subdirectories and files in it. To delete one or more files and directories: DELTREE [/Y] [drive:]path [[drive:]path[...]] /Y Suppresses prompting to confirm you want to delete t +he subdirectory. [drive:]path Specifies the name of the directory you want to dele +te. Note: Use DELTREE cautiously. Every file and subdirectory within the s +pecified directory will be deleted.
      First - he only wants to recursively delete directories that match a particular string. Secondly - deltree is suspiciously missing on Windows 2000 :-(
        Win2K uses `rmdir`, no more deltree (same functionality though)