http://qs321.pair.com?node_id=283452

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

Hi Monks,
I'm trying to get the newest file in a Win32 directory. I have done it as follows:
@list=(`dir *.log \/B \/OD`); $count = scalar(@list); $newest = $list[$#count]; print "$newest \n";
Although this works, I am sure that there is a better way of doing this?!?
Also, is there a module I should know about for these sorts of things??

Thanks,
Smaug.

Replies are listed 'Best First'.
Re: Return newest file
by chromatic (Archbishop) on Aug 13, 2003 at 06:16 UTC

    You can access items in an array with negative indices:

    my @list = `dir *.log \/B \/OD`; my $newest = $list[-1]; print "$newest\n";

    Some people dislike shelling out, though. You could use readdir, sort, and the -X filetest operators to check timestamps. I think it'd look something like this:

    my @files = sort { -M $a <=> -M $b } <*.log>; my $newest = $files[-1]; print "$newest\n";

    That's untested, though, since it's late and I don't run Windows.

Re: Return newest file
by cchampion (Curate) on Aug 13, 2003 at 10:36 UTC

    You can get the last file in one go.

    $last= (sort { -M $b <=> -M $a } <*.log>)[-1];

    If you have a very large number of files, you may consider using the Schwartzian Transform to minimize sorting time.

    $last = ( map { $_->[0] } sort { $b->[1] <=> $a->[1] } map { [ $_, -M $_ ] } <*.log> )[-1];

    Both versions are short enough to be used in a one-liner.

    Update
    Tested in Linux and Windows. Works fine.

      Why torture yourself to grab the last element when you can reverse the sort and grab the first? It strikes me as being semantically clearer as well:
      $newest = (sort { -M $a <=> -M $b } <*.log>)[0];
      Same story with the Schwartz trick.

      Of course, TIMTOWTDI :)

Re: Return newest file
by AcidHawk (Vicar) on Aug 13, 2003 at 06:25 UTC

    stat is your friend

    From the Perldocs..

    Returns a 13-element list giving the status info for a file, either th +e file opened via FILEHANDLE, or named by EXPR. If EXPR is omitted, i +t stats $_. Returns a null list if the stat fails. Typically used as +follows: ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($filename); Not all fields are supported on all filesystem types. Here are the mea +ning of the fields: 0 dev device number of filesystem 1 ino inode number 2 mode file mode (type and permissions) 3 nlink number of (hard) links to the file 4 uid numeric user ID of file's owner 5 gid numeric group ID of file's owner 6 rdev the device identifier (special files only) 7 size total size of file, in bytes 8 atime last access time in seconds since the epoch 9 mtime last modify time in seconds since the epoch 10 ctime inode change time (NOT creation time!) in seconds since t +he epoch 11 blksize preferred block size for file system I/O 12 blocks actual number of blocks allocated
    Here is a quick and dirty hack of how to do it without using dir..

    #! /usr/bin/perl use strict; use warnings; use File::stat; my $dir = $ARGV[0] || "./"; my ($time1, $time2, $returnname); opendir (DIR, $dir) or die "Cannot open $dir: $!\n"; while( my $file = readdir( DIR ) ) { next if $file =~ /^\./; my $sb = stat($file); $time2 = $sb->mtime; if ($time2 > $time1) { $time1 = $time2; $returnname = $file; } #printf "File is %s, size is %s, mtime %s\n", $file, $sb->size, $sb +->mtime; } print "Newest file is $returnname\n";
    Note the mtime is returned as an epoc time so if you want yesterdays file all you have to do is subtract 86400 seconds... type stuff...

    -----
    Of all the things I've lost in my life, its my mind I miss the most.