#!/usr/bin/perl -w # Makes a zip file of the most recent files in a specified directory. # By Rudi Farkas, rudif@bluemail.ch, 9 December 2000 # Usage: # ziprecent -d [-e ...]> [-h] [-msvc] [-q] [] # Zips files in source directory and its subdirectories # whose file extension is in specified extensions (default: any extension). # -d max age (days) for files to be zipped (default: 1 day) # source directory # -e one or more space-separated extensions # -h print help text and exit # -msvc may be given instead of -e and will zip all msvc source files # -q query only (list files but don't zip) # .zip path to zipfile to be created (or updated if it exists) # # $Revision: 1.2 $ use strict; use Archive::Zip qw(:ERROR_CODES :CONSTANTS); use Cwd; use File::Basename; use File::Copy; use File::Find; use File::Path; # argument and variable defaults # my $maxFileAgeDays = 1; my $defaultzipdir = 'h:/zip/_homework'; my ($sourcedir, $zipdir, $zippath, @extensions, $query); # usage # my $scriptname = basename $0; my $usage = < -d [-e ...]> [-h] [-msvc] [-q] [] Zips files in source directory and its subdirectories whose file extension is in specified extensions (default: any extension). -d max age (days) for files to be zipped (default: 1 day) source directory -e one or more space-separated extensions -h print help text and exit -msvc may be given instead of -e and will zip all msvc source files -q query only (list files but don't zip) .zip path to zipfile to be created (or updated if it exists) ENDUSAGE # parse arguments # while (@ARGV) { my $arg = shift; if ($arg eq '-d') { $maxFileAgeDays = shift; $maxFileAgeDays = 0.0 if $maxFileAgeDays < 0.0; } elsif ($arg eq '-e') { while ($ARGV[0] && $ARGV[0] !~ /^-/) { push @extensions, shift; } } elsif ($arg eq '-msvc') { push @extensions, qw / bmp c cpp def dlg dsp dsw h ico idl mak odl rc rc2 rgs /; } elsif ($arg eq '-q') { $query = 1; } elsif ($arg eq '-h') { print STDERR $usage; exit; } elsif (-d $arg) { $sourcedir = $arg; } elsif ($arg eq '-z') { if ($ARGV[0]) { $zipdir = shift; } } elsif ($arg =~ /\.zip$/) { $zippath = $arg; } else { errorExit("Unknown option or argument: $arg"); } } # process arguments # errorExit("Please specify an existing source directory") unless defined($sourcedir) && -d $sourcedir; my $extensions; if (@extensions) { $extensions = join "|", @extensions; } else { $extensions = ".*"; } # change '\' to '/' (avoids trouble in substitution on Win2k) # $sourcedir =~ s|\\|/|g; $zippath =~ s|\\|/|g if defined($zippath); # find files # my @files; cwd $sourcedir; find(\&listFiles, $sourcedir); printf STDERR "Found %d file(s)\n", scalar @files; # exit ? # exit if $query; exit if @files <= 0; # prepare zip directory # if (defined($zippath)) { # deduce directory from zip path $zipdir = dirname($zippath); $zipdir = '.' unless length $zipdir; } else { $zipdir = $defaultzipdir; } # make sure that zip directory exists # mkpath $zipdir unless -d $zipdir; -d $zipdir or die "Can't find/make directory $zipdir\n"; # create the zip object # my $zip = Archive::Zip->new(); # read-in the existing zip file if any # if (defined $zippath && -f $zippath) { my $status = $zip->read($zippath); warn "Read $zippath failed\n" if $status != AZ_OK; } # add files # foreach my $memberName (@files) { if (-d $memberName) { warn "Can't add tree $memberName\n" if $zip->addTree($memberName, $memberName) != AZ_OK; } else { $zip->addFile($memberName) or warn "Can't add file $memberName\n"; } } # prepare the new zip path # my $newzipfile = genfilename(); my $newzippath = "$zipdir/$newzipfile"; # write the new zip file # my $status = $zip->writeToFileNamed($newzippath); if ($status == AZ_OK) { # rename (and overwrite the old zip file if any)? # if (defined $zippath) { my $res = rename $newzippath, $zippath; if ($res) { print STDERR "Updated file $zippath\n"; } else { print STDERR "Created file $newzippath, failed to rename to $zippath\n"; } } else { print STDERR "Created file $newzippath\n"; } } else { print STDERR "Failed to create file $newzippath\n"; } # subroutines # sub listFiles { if (/\.($extensions)$/) { cwd $File::Find::dir; return if -d $File::Find::name; # skip directories my $fileagedays = fileAgeDays($_); if ($fileagedays < $maxFileAgeDays) { printf STDERR "$File::Find::name (%.3g)\n", $fileagedays; (my $filename = $File::Find::name) =~ s/^[a-zA-Z]://; # remove the leading drive letter: push @files, $filename; } } } sub errorExit { printf STDERR "*** %s ***\n$usage\n", shift; exit; } sub mtime { (stat shift)[9]; } sub fileAgeDays { (time() - mtime(shift)) / 86400; } sub genfilename { my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); sprintf "%04d%02d%02d-%02d%02d%02d.zip", $year + 1900, $mon + 1, $mday, $hour, $min, $sec; } __END__ =head1 NAME ziprecent.pl =head1 SYNOPSIS ziprecent h:/myperl ziprecent h:/myperl -e pl pm -d 365 ziprecent h:/myperl -q ziprecent h:/myperl h:/temp/zip/file1.zip =head1 DESCRIPTION This script helps to collect recently modified files in a source directory into a zip file (new or existing). It uses Archive::Zip. =over 4 =item C< ziprecent h:/myperl > Lists and zips all files more recent than 1 day (24 hours) in directory h:/myperl and it's subdirectories, and places the zip file into default zip directory. The generated zip file name is based on local time (e.g. 20001208-231237.zip). =item C< ziprecent h:/myperl -e pl pm -d 365 > Zips only .pl and .pm files more recent than one year. =item C< ziprecent h:/myperl -msvc > Zips source files found in a typical MSVC project. =item C< ziprecent h:/myperl -q > Lists files that should be zipped. =item C< ziprecent h:/myperl h:/temp/zip/file1.zip > Updates file named h:/temp/zip/file1.zip (overwrites an existing file if writable). =item C< ziprecent -h > Prints the help text and exits. ziprecent.pl -d [-e ...]> [-h] [-msvc] [-q] [] Zips files in source directory and its subdirectories whose file extension is in specified extensions (default: any extension). -d max age (days) for files to be zipped (default: 1 day) source directory -e one or more space-separated extensions -h print help text and exit -msvc may be given instead of -e and will zip all msvc source files -q query only (list files but don't zip) .zip path to zipfile to be created (or updated if it exists) =back =head1 BUGS Tested only on Win2k. Does not handle filenames without extension. Does not accept more than one source directory (workaround: invoke separately for each directory, specifying the same zip file). =head1 AUTHOR Rudi Farkas rudif@lecroy.com rudif@bluemail.ch =head1 SEE ALSO perl ;-) =cut