Open a directory and sort files by date created

18,568

Solution 1

You can customize the sorting order by providing a subroutine or code block to the sort function.

  • In this sub or block, you need to use the special variables $a and $b, which represent the values from the @array as they are compared.
  • The sub or block needs to return a value less than, equal to, or greater than 0 to indicate whether $a is less than, equal to, or greater than $b (respectively).
  • You may use the special comparison operators (<=> for numbers, cmp for strings) to do this for you.

So the default sort sort @numbers is equivalent to sort {$a <=> $b} @numbers.

In the case of sorting by creation time, you can use the stat function to get that information about the file. It returns an array of information about the file, some of which may not be applicable to your platform. Last modification time of the file is generally safe, but creation time is not. The ctime (11th value that it returns) is as close as you can get (it represents inode change time on *nix, creation time on win32), which is expressed as the number of seconds since the epoch, which is convenient because it means you can do a simple numeric sort.

my @files = sort {(stat $a)[10] <=> (stat $b)[10]} readdir($dh);

I'm not sure if you want to filter out the directories also. If that is the case, you'll probably also want to use grep.

Solution 2

I need to open directories and sort the files by the time they were created.

You can't. The creation time simply does not exist. There are three time elements tracked by *nix like operating systems:

  • mtime: This is the time the file was last modified.
  • atime: This is the time the file was last accessed.
  • ctime: This is the time when the inode was last modified.

In Unix, certain file information is stored in the inode. This includes the various things you see when you take the Perl stat of a file. This is the name of the user, the size of the file, the device it's on, the link count, and ironically, the mtime, atime, and ctime timestamps.

Why no creation time? Because how would you define it? What if I move a file? Should there be a new creation time (By the way, ctime won't change with a move). What if I copy the file? Should the new copy have a new creation time? What if I did a copy, then deleted the original? What if I edited a file? How about if I changed everything in the file with my edit? Or I edited the file, then renamed it to a completely new name?

Even on Windows that has a file creation time, doesn't really track the file creation. It merely tracks when the directory entry was created which is sort of what ctime does. And, you can even modify this creation time via the Windows API. I suspect that the Mac's file creation time is a relic of the HFS file system, and really doesn't point to a file creation time as much as the time the directory entry was first created.


As others have pointed out. You can add into the sort routine a block of code stating how you want something sorted. Here's a quickie example. Note I use File::stat which gives me a nice by name interface to the old stat command. If I used the old stat command, I would get an array, and then have to figure out where in the array the item I want is located. Here, the stat command gives me a stat object, and I can use the mtime, atime, or ctime method for pulling out the right time.

I also use the <=> which is a comparison operator specifically made for the sort command block.

The sort command gives you two items $a and $b. You use these two items to figure out what you want, adn then use either <=> or cmp to say whether $a is bigger, $b is bigger, or they're both the same size.

#! /usr/bin/env perl

use 5.12.0;
use warnings;

use File::stat;

my $dir_name = shift;

if ( not defined $dir_name ) {
    die qq(Usage: $0 <directory>);
}

opendir(my $dir_fh, $dir_name);

my @file_list;
while ( my $file = readdir $dir_fh) {
    if ( $file !~ /^\./ ) {
        push @file_list, "$dir_name/$file"
    }
}
closedir $dir_fh;

say scalar @file_list;

for my $file (sort {
        my $a_stat = stat($a);
        my $b_stat = stat($b);
        $a_stat->ctime <=> $b_stat->ctime;
    }  @file_list ) {
    say "$file";
}

Solution 3

OS X stores the creation date in Mac-specific metadata, so the standard Perl filesystem functions don't know about it. You can use the MacOSX::File module to access this information.

Solution 4

#!/usr/bin/env perl
use strict;
use warnings;
opendir(DIR, $ARGV[0]);
chdir($ARGV[0]);
my @files = sort { (stat($a))[10] <=> (stat($b))[10] } (readdir(DIR));
closedir(DIR);
print join("\n",@files);

stat gives you all kinds of status info for files. field 10 of that is ctime (on filesystems that support it) which is inode change time (not creation time).

Share:
18,568
ES55
Author by

ES55

Updated on June 04, 2022

Comments

  • ES55
    ES55 almost 2 years

    I need to open directories and sort the files by the time they were created. I can find some discussion, using tags for Perl, sorting, and files, on sorting files based on date of modification. I assume this is a more common need than sorting by date of creation. I use Perl. There is some previous postings on sorting by creation date in other languages other than Perl, such as php or java.

    For example, I need to do the following:

    opendir(DIR, $ARGV[0]);                             
    my @files = "sort-by-date-created" (readdir(DIR)); 
    closedir(DIR);
    
    do things with @files...
    

    The CPAN has a page on the sort command, but it's not very accessible to me, and I don't find the words "date" or "creation" on the page.

    In response to an edit, I should say I use Mac, OS 10.7. I know that in the Finder, there is a sort by creation date option, so there must be some kind of indication for date of creation somehow attached to files in this system.

    In response to an answer, here is another version of the script that attempts to sort the files:

    #!/usr/bin/perl
    use strict; use warnings;
    
    use File::stat; # helps with sorting files by ctime, the inode date that hopefully can serve as creation date
    
    my $usage = "usage: enter name of directory to be scanned for SNP containing lines\n";
    die $usage unless @ARGV == 1;
    
    opendir(DIR, $ARGV[0]);                             #open directory for getting file list
    #my @files = (readdir(DIR));
    my @file_list = grep ! /^\./, readdir DIR; 
    closedir(DIR);  
    
    print scalar @file_list."\n";
    
    for my $file (sort {
            my $a_stat = stat($a);
            my $b_stat = stat($b);
            $a_stat->mtime <=> $b_stat->mtime;
        }  @file_list ) {
        say "$file";
    }
    
  • Barmar
    Barmar about 11 years
    ctime is inode change time, not creation time.
  • Barmar
    Barmar about 11 years
    Also, your arguments to stat() are wrong. They should be $ARGV[0].'/'.$a and $ARGV[0].'/'.$b
  • Emile Aben
    Emile Aben about 11 years
    @barman : I've addd chdir to account for that. '/' is not a universal directory separator. and added comment about ctime not being creation time.
  • ES55
    ES55 about 11 years
    what if i switched over to a PC? What could I do in perl in that case?
  • ES55
    ES55 about 11 years
    I need to sort by date or time of creation, not by modification time. That seems to be a more common task, but in my case, I need to sort by creation time.
  • ES55
    ES55 about 11 years
    I don't understand "filter out the directories". What does this mean? To ignore them during a sorting of files?
  • stevenl
    stevenl about 11 years
    By 'filter' I mean to exclude the directories from your list.
  • Barmar
    Barmar about 11 years
    I found that module by googling. I guess you could do the same thing.
  • Jonathan Leffler
    Jonathan Leffler about 11 years
    Inode change time is supported in Mac OS X, as is file creation time. The difficulty is getting at the file creation time in Perl.
  • Jonathan Leffler
    Jonathan Leffler about 11 years
    The MacOSX::File module was last updated in 2005. I just tried installing it on Mac OS X 10.7.5 (Perl 5.16.2) and it failed to compile. A pity...it probably can be fixed, but I've not done so yet. I use a home-brew version of GCC and a home-built Perl; this may be the problem, but the error message I got was /Developer/Headers/FlatCarbon/strings.h:1:2: warning: #warning Strings.h is not available on Mac OS X [-Wcpp].
  • Barmar
    Barmar about 11 years
    Oops. Just checked the blog the pointed me to it, it's from 2002!
  • stevenl
    stevenl about 11 years
    The problem is that some filesystems just don't keep that information
  • ES55
    ES55 about 11 years
    About "inode"...So, in the Finder window, when I select to arrange files by "date created," I am really asking the computer to sort the files by "ctime", is that correct?
  • ES55
    ES55 about 11 years
    Also, when you use use 5.12.0;, is that to enable the say command? Or does use 5.12.0; have another or more general purpose here?
  • David W.
    David W. about 11 years
    When you use use 5.12.0 (or above), you enable all the various new features, plus you automatically turn on strict. I don't know why warnings isn't also automatically turned on, but it isn't.
  • David W.
    David W. about 11 years
    Perl was first written for Unix, so it has a Unix bias. You need to see perlport for these type of edge cases. Perlport says on Windows ctime=Win32 creation time, but is silent on MacOS X w/ HFS+. The Mac manpages are little use. There is a MacOSX::File::Info CPAN module that fetches the creation-date. Unfortunately, it won't install on my MacBook Pro. If you can use MacOSX::File::Info, you could use that instead of File::stat or at least compare ctime on File::stat to Creation time on MacOSX::File:Info
  • ES55
    ES55 about 11 years
    Hi, I get this when I try your script lines: Can't call method "mtime" on an undefined value at snpParsing.pl line 29.
  • David W.
    David W. about 11 years
    A bit more investigating... I couldn't install MacOSX::File because I am missing Files.h. File.h was a Carbon header file, but Carbon is no more. MacOSX::File may no longer be supported. The Mac dev environment has a lot of dross. The HFS+ file system was not written for Unix. HFS+ things like File forks, File Creators, and File Types are on MacOSX, but aren't generally supported and don't have Unix equivalents.
  • David W.
    David W. about 11 years
    Mac::File depends upon the old Carbon Framework, but Mac OS X no longer supports the Carbon Framework. The Carbon framework was there to help developers port their programs over from the old System 7/8/9 to MacOS X. However, Apple stopped putting supporting it a few OS X releases ago.
  • ES55
    ES55 about 11 years
    Hi, so what I meant to say was that I tried your File::Stat and sort method that you described up in your first answer. The error message "Can't call method "mtime" on an undefined value at snpParsing.pl line 29." applies to that. Or, is the File::Stat and 'sort` method related to this MacOSX::File method? Are those the same thing? In other words, does your initial answer not work?
  • David W.
    David W. about 11 years
    @ES55 - Try cutting out my script as is and trying it on your system. I am using a MacBook Pro with the latest OS X version. It works fine on mine. I can't see what line #29 of your script is, but you must do a $file_stat_obj = stat($file); before you can call $file_stat_obj->mtime. What version of Perl are you using?
  • ES55
    ES55 about 11 years
    v 5.12.3. When I cut and paste, after replacing "." with an actual directory name, I get an error about it not being able to call method "mtime" on an undeclared value. I can paste the code I tried earlier in my question so you can see what line# 29 is about.
  • David W.
    David W. about 11 years
    You need to include the directory name when you push the file name into $file_list. See my modified program in my answer.
  • jsh
    jsh about 9 years
    mtime is index 9, and i found i needed more parens, e.g. ((stat $path/$a))[9]