Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?

READDIR - Sort by Time?

by Anonymous Monk
on Sep 24, 2002 at 13:48 UTC ( [id://200363]=perlquestion: print w/replies, xml ) Need Help??

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

I need to read a directory with it sorted by time created for processing. I was wondering if there is an easier way (by using READDIR or a similar function) than creating a hash of the directory and referencing the $mtime?

My current code:

my @dirfiles = grep !/^\.\.?$/, readdir ORDERS;


Replies are listed 'Best First'.
Re: READDIR - Sort by Time?
by Aristotle (Chancellor) on Sep 24, 2002 at 13:59 UTC
    Not really, no. You could look up the mtime directly in the sort callback (see perldoc -f sort), but stat()ing files is an expensive operation and sort tends to take N*log(N) iterations, so you'd be doing too much work that way. The only way to do this efficiently would be to look the mtimes up first in a single pass. That could be done using the Schwartzian Transform.
    my @dirfiles = map $_->[0], sort { $a->[1] <=> $b->[1] } map [ $_, +(stat "$dirname/$_")[9] ], grep $_ ne '.' and $_ ne '..', readdir ORDERS;

    Note that using a regex to filter out the . and .. here is overkill and even results in unexpected behaviour: the $ matches in front of a \n so the regex will also throw away files called ".\n" or "..\n".

    Update: changed stat $_ (d'oh) into stat "$dirname/$_"

    Makeshifts last the longest.

Re: READDIR - Sort by Time?
by BrowserUk (Patriarch) on Sep 24, 2002 at 14:12 UTC

    The only other way I can think of (others may know better) would be to parse the output from ls or dir with the appropriate sort options. but that is much harder than using a hash to control a sort.

    #! perl -sw use strict; my %times; opendir DIR, $ARGV[0] or die "Couldn't open $ARGV[0]; $!\n"; my @sorted = sort { $times{$b} <=> $times{$a}; # Was cmp D'oh! Thanks [dws] } map { $times{$_} = (stat $_)[9]; $_; } grep{ -f $_ } readdir DIR; closedir DIR or warn "Couldn't close $ARGV[0]; $!\n"; local $" = $/; print "@sorted\n";

    You might need to change the stat index from 9 to either 8 or 10 depending upon your needs.

    I also think that testing using -f is safer than using your match to rid yourself of '.' and '..', though if your on a system that supports and uses symbolic links for example, you might need to bolster the grep test.

    Oh! And if you want them oldest first, switch the order of $a and $b of course.

    Updated to correct cmp -v- <=> error. Thanks dws.

    Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!
      There's an interesting failure mode embedded in
      sort { $times{$b} cmp $times{$a}; } map { $times{$_} = (stat $_)[9]; $_; }
      That you won't notice if all of your files are recent, but will cause odd results if one or more of your files predate 6:46:40pm, September 8, 2001.

      See it?

        Yes, though the 'clue' threw me for a few seconds. Thank &deity; for Date::Manip::UnixDate( '%s', 'datestring' );

        Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!

        Though the breakpoint appears to be somewhat different?

        perl> print UnixDate( '2:46:40am, September 9, 2001.', '%s') 1000000000 perl>

        Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!
Re: READDIR - Sort by Time?
by thelenm (Vicar) on Sep 24, 2002 at 15:08 UTC
    Just a minor point in case you weren't already aware of it... you said you need to sort the filenames by "time created", but you're using the mtime, which is the "last modify time". But I don't know if you can do any better than that, since AFAIK there is no way to retrieve the original creation time of a file (at least under Linux). See perldoc -f stat for more details.

    -- Mike


Re: READDIR - Sort by Time?
by twerq (Deacon) on Sep 24, 2002 at 14:00 UTC
    my @dirfiles = sort { -M $a <=> -M $b } grep !/^\.\.?$/, readdir ORDERS;

    should work, I guess. Don't forget to prepend $a and $b with the directory path you opened (if you're not working in ./).


    Update: Aristotle's solution is better. :)
Re: READDIR - Sort by Time?
by diotalevi (Canon) on Sep 24, 2002 at 14:06 UTC

    Um... no. readdir() is unsorted and you don't want to be making redundant stat() calls (being expensive n' all).

    my @dirfiles = map { $_->[0] } sort { $_->[1] <=> $_->[1] } map { [ $_, stat $_ ] } grep( not m/^\.\.?$/, readdir ORDERS);
Re: READDIR - Sort by Time?
by fglock (Vicar) on Sep 24, 2002 at 14:10 UTC

    The quick and dirty way (security, performance and portability restrictions apply):

    @dir = `ls -c`; chomp @dir;
Re: READDIR - Sort by Time?
by fundflow (Chaplain) on Sep 24, 2002 at 14:04 UTC
    Instead of readdir it is possible to use <*>

    Here is a snippet:

    my @e = sort { (stat($a))[9] <=> (stat($b))[9] } <*> ;

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://200363]
Approved by guha
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (2)
As of 2024-07-14 01:42 GMT
Find Nodes?
    Voting Booth?

    No recent polls found

    erzuuli‥ 🛈The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.