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

i am new to perl, can some one tell me how can i count number of files created yesterday in a directory ? in directory i can see 1000+ files , i need the count of files created only for yesterday , Need help to write a code , can some one help me ?, I started with the below code
#!/usr/bin/perl use strict; my $dir = "*****"; my $directory_count = 0; my $file_count=0; opendir(DIR, $dir); LINE: while(my $FILE = readdir(DIR)) { next LINE if($FILE =~ /^\.\.?/); if(-d $FILE){ $directory_count++; } else { $file_count++; } } closedir(DIR); print "Directories: $directory_count\n"; print "Files: $file_count\n"; exit;

Replies are listed 'Best First'.
Re: To count number of files created yesterday in a directory ( file find yesterday)
by beech (Parson) on Feb 03, 2017 at 03:18 UTC
Re: To count number of files created yesterday in a directory
by haukex (Archbishop) on Feb 03, 2017 at 07:41 UTC

    Hi ajy,

    Please use <code> tags around your code so that it gets displayed properly. See also How do I post a question effectively?.

    Note that the core module File::stat makes stat (which beech already recommended) a bit easier to use. At the top of your program, use File::stat;, then to get a file's creation time as a UNIX timestamp, you simply need to call stat($filename)->ctime. The Perl function time will give you the current time as a UNIX timestamp, so you just need to compare them.

    Since you're using readdir, note that it's necessary to prepend the directory name to the filenames that readdir returns. In your example, at the top of the program say use File::Spec::Functions qw/catfile/;, then in the loop you just need to say my $filename = catfile($dir, $FILE);. Also, it's possible to use File::Spec's no_upwards function to skip the directories . and .. instead of the regex /^\.\.?/ (Update: Although, note that this regex also causes filenames beginning with a dot to be skipped, not sure if that's what you want?).

    There are other ways to list files in a directory, for example, there's glob (note that by default, that skips files whose name begin with a dot), or maybe Path::Class::Dir's children function.

    One more note: you should use warnings; at the top of your script, and check the return value of opendir for errors, and I'd recommend lexical file/directory handles, as in opendir(my $dirhandle, $dir) or die "opendir $dir: $!"; - then use $dirhandle instead of DIR.

    Hope this helps,
    -- Hauke D

Re: To count number of files created yesterday in a directory
by Marshall (Canon) on Feb 03, 2017 at 17:40 UTC
    I will present a modified version of your code, then some explanation.
    #!/usr/bin/perl use strict; use warnings; my $dir = "."; my $directory_count = 0; my $file_count=0; my $changed_yesterday_count=0; opendir(DIR, $dir) or die "unable to open $dir"; while(my $FILE = readdir(DIR)) { next if($FILE =~ /^\.$|^\.\.$/); # .file_name is "hidden" file # . and .. are standard director +ies if(-d "$dir/$FILE") #need to use a full path { $directory_count++; } elsif (-f _) #simple file (not link,pipe,etc) { $file_count++; } if (-C _ == 1) # inode changed one day ago { # see notes below, this is # not perfect $changed_yesterday_count++; } } closedir(DIR); print "Directories: $directory_count\n"; print "Files: $file_count\n"; print "Dirs/files changed 1 day ago: $changed_yesterday_count\n"; __END__ example print: Directories: 5 Files: 368 Dirs/files changed 1 day ago: 0
    • Others have pointed out a potential flaw in you regex to skip the . and .. directories
    • readdir() only returns the file names, not full path. You need to create the full path for use with a file test or other operation.
    • A test like -d "$dir/$FILE" actually fetches the whole stat data structure which is relatively "expensive". To do another test on that exact same file, the special variable "_" can be used. That just looks at the cached result of the previous file test operation. This just makes further file tests run faster because the file system doesn't need to be accessed again.
    • There are more than just files and directories, there are other things like pipes and symbolic links. So, just because something is not a directory, that does mean that it is for sure a simple file. In most cases that will be true, but not always.
    • Unix does not have a file "creation time". I would call that the "born on" date. This is available on a Windows file system, but requires use of a special api. In almost all cases on Unix, you are interested in the inode change time. This time changes if the file is modified or if the permissions (user/group/others) is modified.
    • For your application, looks to me like the -C file test will do the trick. This yields how long ago the ctime changed measured in days.
    • Some links for further study:
      file stat function
      file test operators
      Please explain -C inode
      can google "st_ctime"
Re: To count number of files created yesterday in a directory
by Cristoforo (Curate) on Feb 04, 2017 at 00:31 UTC
    Using Time::Piece and Time::Seconds, (in the core since perl version 5.9.5), you can get the count of files created yesterday with the code below.
    #!/usr/bin/perl use strict; use warnings; use Time::Piece; use Time::Seconds; my $t = localtime() - ONE_DAY; # yesterday my $count = grep {$t->ymd eq localtime( (stat)[9] )->ymd} glob "*.*";
    (I was not able to use the code below, even though the documentation for Time::Piece says '==' is possible.)

    my $count = grep {$t == localtime( (stat)[9] )} glob "*.*";

    Update: Of course it wouldn't work as it's comparing 'ymdhms' rather than 'ymd'.

      glob "*.*";

      Note that the above glob will only match files/dirs containing a dot. If memory serves, this is a Microsoftism because in their world a simple glob "*" doesn't match all entries like it would on other platforms.

      Luckily there are things like File::Glob to help with making such actions platform-independent.

        Hi hippo,

        a simple glob "*" doesn't match all entries like it would on other platforms

        At least on Linux, glob "*" skips entries beginning with a dot, to get those you need glob ".* *".

        Regards,
        -- Hauke D