Skip to content

Instantly share code, notes, and snippets.

@thinkhy
Last active December 10, 2015 01:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thinkhy/4361976 to your computer and use it in GitHub Desktop.
Save thinkhy/4361976 to your computer and use it in GitHub Desktop.
Exercise for Chapter 7 of "Intermediate Perl"
#########################################################################
# Brief: Exercise for Chapter 7 of "Intermediate Perl"
# File: ch7_exam.pl
# Creator: thinkhy
# Date: 12/23/2012
#########################################################################
use File::Find;
use Time::Local;
use File::Spec::Functions qw(canonpath no_upwards);
my $target_dow = 2; # Sunday is 0, Monday is 1, ...
my @starting_directories = (".", "e:/tmp");
my $seconds_per_day = 24 * 60 * 60;
my($sec, $min, $hour, $day, $mon, $yr, $dow) = localtime;
my $start = timelocal(0, 0, 0, $day, $mon, $yr); # midnight today
while ($dow != $target_dow) {
# Back up one day
$start -= $seconds_per_day; # hope no DST! :?)
if (--$dow < 0) {
$dow += 7;
}
}
my $stop = $start + $seconds_per_day;
my($gather, $yield) = gather_mtime_between($start, $stop);
find($gather, @starting_directories);
my @files = $yield->( );
for my $file (@files) {
my $mtime = (stat $file)[9]; # mtime via slice
my $when = localtime $mtime;
print "$when: $file\n";
}
sub gather_mtime_between {
my $start_time = shift;
my $end_time = shift;
my @files = ();
#########################################################################
# Here use canonpath, details explained as below
# canonpath:
# No physical check on the filesystem, but a logical cleanup of a path.
# no_upwards:
# Given a list of file names, strip out those that refer to a
# parent directory. (Does not strip symlinks,
# only '.', '..', and equivalents.)
# Refer to http://perldoc.perl.org/File/Spec.html
#
# Also, notice that when using stat inside the callback, the filename is $_
# but when the filename reperting to the user, the name is $File::Find::name
# because different current working directory for callback and user function
#########################################################################
sub {
my $when = (stat $_)[9];
push @files, canonpath( $File::Find::name )
if ($when >= $start_time and $when <= $end_time) },
sub { @files = no_upwards( @files );
wantarray ? @files : [ @files ] }
}
# Here is the 'official' answer for Chapter 7
sub gather_mtime_between {
my($begin, $end) = @_;
my @files;
my $gatherer = sub {
my $timestamp = (stat $_)[9];
unless (defined $timestamp) {
warn "Can't stat $File::Find::name: $!, skipping\n";
return;
}
push @files, $File::Find::name
if $timestamp >= $begin and $timestamp <= $end;
};
my $fetcher = sub { @files };
($gatherer, $fetcher);
}
############################################################################
# Compared with answer, I omitted a special condition that the timestamp
# will be undef if the stat fails for some reason. That can happen, for example,
# if it finds a dangling symbolic link(the target file doesn't exist).).
# In that case, the callback warns the user and returns early.
# If we omit that check, we can get warnings of an undefined value
# during the comparison with begin time and end time.
############################################################################
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment