Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Finding Oldest File in a Directory

by awohld (Hermit)
on Oct 04, 2005 at 20:47 UTC ( [id://497400]=perlquestion: print w/replies, xml ) Need Help??

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

I'm trying to find the oldest file in a directory whose name contains the word "BOT", but when I have multiple files to look at and I try to find "mtime" from File::Stat, it tells me that I can't call "mtime" on an undefined value.

I don't understand why when I'm looping through the file names they come back as undefined?

Here's my code, if you change the directory name and run it, you should see what I mean:
#!/usr/bin/perl -w use File::stat; use CGI; use strict; my $cgi = new CGI; print $cgi->header; my $dirname = '/www/htdocs/reports/bot/'; my $oldestfile = 0; my $filename = "No BOT REPORT: ERROR"; opendir(DIR, $dirname) or die "can't opendir $dirname: $!"; while (my $file = readdir(DIR)) { if ($file =~ /BOT/){ my $x = stat($file)->mtime; if ( $x > $oldestfile) { $oldestfile = $x; $filename = $file; } } } print <<EOF; <a href="http://192.168.0.1/reports/bot/$filename"><strong>$filename</ +strong></a> - BOT Report.<br> EOF closedir(DIR);

Replies are listed 'Best First'.
Re: Finding Oldest File in a Directory
by samtregar (Abbot) on Oct 04, 2005 at 21:03 UTC
    You're calling stat on files within $dirname, but you're not in $dirname. Change your stat call to:

       my $x = stat("$dirname/$file")->mtime;

    Alternately you could chdir() to $dirname and make your stats the same way you are now.

    -sam

      Yep. It would be better yet to use File::Spec though.

      use File::Spec::Functions qw( catfile ); # ... my $x = stat( catfile $dirname, $file )->mtime;

      Makeshifts last the longest.

Re: Finding Oldest File in a Directory
by GrandFather (Saint) on Oct 04, 2005 at 21:01 UTC

    $file =~ /BOT/ will never succeed because readdir returns the leaf name, not the whole path so you get BOT rather than /www/htdocs/reports/bot/.

    For something like this I'd be inclined to use File::Find in any case.


    Perl is Huffman encoded by design.

      Why are you suggesting File::Find? It recurses (unless you kludge it), which is not what the OP wants, and it does not help at all with checking file metadata, which is what the OP needs.

      Makeshifts last the longest.

        Because using File::File::name gets you a file name including the path so that -M or stat can find the file. If you don't want to recurse into directories use:

        if (-d Find::File:name) { File::Find::prune = 1; return; }

        Perl is Huffman encoded by design.
Re: Finding Oldest File in a Directory
by davido (Cardinal) on Oct 04, 2005 at 20:55 UTC

    Add this and see what happens:

    if( -f $file and $file =~ /BOT/ ) { # ^^^^^^^^ <----- Add this test.

    I think the problem is that readdir returns everything in the directory, including other directories.

    By the way; mtime is not the same as the file's age.


    Dave

Re: Finding Oldest File in a Directory
by sk (Curate) on Oct 04, 2005 at 21:09 UTC
    Update: Please note that this solution searches for the files recursively. Thanks to aristotle for the clarification

    Use File::Find

    Untested

    #!/usr/bin/perl -w use File::Find; use strict; my $dir = '/path/to/dir/'; my $oldestfile = 0; my $filename = "No BOT REPORT: ERROR"; finddepth(\&oldfile, $dir); print "oldest file = " , $filename,$/; sub oldfile { return unless (/BOT/); my $mtime = -M $_; if ($mtime > $oldestfile) { $filename = $File::Find::name; $oldestfile = $mtime; } }

    BTW i think you are not getting the files using the readdir due to the context

    from perldoc -f readdir

    readdir DIRHANDLE Returns the next directory entry for a directory opened by "opendir". +If used in list context, returns all the rest of the entries in the d +irectory. If there are no more entries, returns an undefined value i +n scalar context or a null list in list context.

      He didn’t say he wants to recurse, nor does his example code indicate anything like that. Why are you using File::Find?

      Makeshifts last the longest.

        It has been a while since i posted this - I think i looked at the OP's readdir() and somehow felt he is doing some kind of recursion as for just checking the oldest file in a directory can be accomplished using glob or just use the Plain Old Shell and call the script with a * (check for -d etc.) or ls -lrt

        That said, I agree File::Find might not be the best way to solve this problem but it can be used to achieve what the OP wants as GrandFather has shown or just eval finddepth and die on finding a directory as finddepth works bottoms-up.

        I just find it much easier to use File::Find and it is very easy to expand (if recursion is ever required).

        I am not worried too much about performance in this case even if it is 4X times slower. How many times do i have to remove the oldest file? Am i working with 1000s of files? etc. The OP has not mentioned anything about perormance so i am presuming that arg is not very important.

        I definitely DO NOT disagree with your comment(s). It is my mistake not noticing that OP did not ask for recursion, I will update my node saying it does recursion and it is upto the OP to choose it or not.

        cheers

        SK

Re: Finding Oldest File in a Directory
by Zed_Lopez (Chaplain) on Oct 04, 2005 at 21:47 UTC

    Don't forget good ol' glob.

    #!/usr/bin/perl -w use strict; my ($age, $file) = (0, ''); for (<*BOT*>) { if (-M > $age) { $age = -M; $file = $_ } } print "$file\n";
Re: Finding Oldest File in a Directory
by tbone1 (Monsignor) on Oct 05, 2005 at 20:48 UTC
    If you're on *nix, you don't need that. Just use:
    ls -t | head -1
    in the shell/at the command line. (That is a "dash one", not a "dash lowercase ell" on the head command, BTW.)

    --
    tbone1, YAPS (Yet Another Perl Schlub)
    And remember, if he succeeds, so what.
    - Chick McGee

      almost...
      ls -t *BOT* | head -1
      or converted to a one liner in perl...
      perl -e 'my %bot; for ( sort { ( $bot{ $a } ||= ( -M $a ) ) <=> ( $bot +{ $b } ||= ( -M $b ) ) } glob "\*BOT\*" ) { print "$_\n"; last; }'

      -M

      Free your mind

        almost...
        ls -td *BOT* | head -1
        Or you'll get the first file in the oldest directory containing BOT in its name (assuming no file whose name contains BOT is older)

        This still has the possibility of returning a directory name though. Under Unix, this still is a file, although when people say "file", they usually want to exclude directories.

        Perl --((8:>*

      Except you need tail, because ls -t lists the files from newest to oldest.

      And beware of filenames with embedded newlines, they will get cut in half by the tail and you might end up manipulating a file other than the one you wanted.

      Makeshifts last the longest.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://497400]
Approved by Old_Gray_Bear
Front-paged by grinder
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (7)
As of 2024-04-19 07:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found