Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Reading from ls-lrt (was: Reading)

by azatoth (Curate)
on Feb 05, 2001 at 20:36 UTC ( #56419=perlquestion: print w/replies, xml ) Need Help??

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

Is there a simple way to do something like this?

Basically, I am trying to write a script that when invoked, will 'do' an ls -lrt on the chosen directory, grab the last five lines (i.e most recently updated files) and strip out all the garbage, leaving just the filenames. My script will then use ftp to put these files on a remote server.

Am I making any sense? I don't want to post any code yet because the code i do have does not relate to this question - i.e., there is a big blank hole in it and i am looking for some guidance :P

Thanks as always for any responses...

Azatoth a.k.a Captain Whiplash

Get YOUR PerlMonks Stagename here!
Want to speak like a Londoner

Edited by Corion : Fixed title, font tags

Replies are listed 'Best First'.
Re: Reading
by mr.nick (Chaplain) on Feb 05, 2001 at 20:47 UTC
    Instead of spawning an "ls" process, why not use only Perl stuff?:
    #!/usr/bin/perl sub getlast5 { my $dir=shift; my @files; opendir DIR,$dir || die "Couldn't open '$dir': $!"; for my $x (sort { (stat($b))[9] <=> (stat($a))[9] } readdir DIR) { next if $x eq "." || $x eq ".."; push @files,$x; last if $#files==4; } closedir DIR; @files; } print join("\n",getlast5(".")),"\n";
    (it's pretty simple code, but I'm trying different styles :)
      You may want to move the readdir DIR step to a point just prior to the sort so that you can properly filter the directory there... perhaps using a glob or File::Find. This way we can explicitly include only the file types we want, rather than excluding the . and .. (what about .bash_history or such files?) inside the for loop.
        I presume that files beginning with a dot where still eligable for transfer as he didn't say anything about that. But if he's mirroring an FTP site, I suppose a
        next if $x=~/^\./;
        would be appropriate (and take care of the directory stuff).
        You be right, ichimunki, by using the example of 'ls -ltr', it doesn't include dot files ;)
      opendir DIR,$dir || die "Couldn't open '$dir': $!";

      For the main point of your code, I think it's excellent the way that you've used sort() on stat() .
        But, I hope it's helpful to point out that the opendir() statement is broken, as it was written at the time of my response, because it tests $dir for Truth rather than opendir() , and that's not what you want.

      To use the perlop '||' with opendir() the parentheses are not optional. Otherwise, use or which is more reliable in statements such as open() and opendir() , because the precedence of or is so low that it doesn't even require those pesky parentheses.

      (deleted my own bad -here)

      But, as I said, this is a small tweak that shouldn't take away from what seems to me otherwise to be a good idea.

Re: Reading from ls -lrt
by chipmunk (Parson) on Feb 05, 2001 at 20:56 UTC
    You don't need to use the -l (letter ell) option if you just want the filenames. You can use -1 (numeral one) to specify one file per line, but that's the default behavior of ls when output isn't going to a terminal anyway.

    Additionally, if you want the most recent files, instead of using -r and getting the last five files, you should use the default sort order and get the first five files. Thus:

    my @files = (`ls -1t`)[0..4]; chomp(@files);

    However, it might be preferable to do all this in Perl, and avoid spawning a shell and relying on the behavior of ls.

    opendir(DIR, '.') or die "Can't opendir '.': $!\n"; my @files = readdir(DIR); closedir(DIR); @files = map $_->[0], sort { $b->[1] <=> $a->[1] } map { /^\./ ? () : [ $_, (lstat $_)[9] ] } @files; @files = @files[0..4];
(ichimunki) Re: Reading directories
by ichimunki (Priest) on Feb 05, 2001 at 20:47 UTC
    opendir, readdir and stat should make all this a lot more portable and allow you to be more explicit with what you are doing.

    update: Heh. And Mr. Nick has already written a great example of this!
Re: Reading
by ChOas (Curate) on Feb 05, 2001 at 20:40 UTC

    Quick, and NOT the Perl way I prefer:
    my @File=map {(split)[-1]} `ls -ltr|tail -5`;


    p.s. TIMTOWTDI: This is not my prefered way, but it is one.. and simple
    print "profeth still\n" if /bird|devil/;
Re: Reading
by kschwab (Vicar) on Feb 05, 2001 at 21:01 UTC
    • You want to skip "." files and directories
    • You need the 5 "newest" files
    • You want only "regular" files
    opendir(DIR,$somedir) or die; @files=reverse sort {(stat($a))[9] <=> (stat($b))[9]} grep(!/^\./ && -f "$somedir/$_" ,readdir(DIR)); ( $#files > 4 ) && { @files=@files[0..4] }; closedir(DIR);
Re: Reading ls -lrt
by baku (Scribe) on Feb 05, 2001 at 20:57 UTC

    Gave me a chance to learn opendir and map :-)

    opendir LSLRT, "$dir" or die "I hate you, $dir: $!"; # rewinddir not needed @dir = readdir(LSLRT); closedir LSLRT; # why 'or die,' if we're all done anyways? for my $dirent (@dir) { next unless ( -f "$dir/$dirent" ); $mtime{$dirent} = (stat "$dir/$dirent")[9]; } my @last_five = (map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [$_, $mtime{$_} ] } keys %mtime)[-5..-1];

    This is evil because I sort the whole thing and then throw away most of it.

    For easy-to-maintain:

    my @last_five = chomp (`ls -1 --sort=time`)[0..4];

    Corrections: Don't need to reverse in the ls -1 form, and don't need to rewinndir.

    N.B. that I use -1 because CygWin's ls will send ANSI colour codes if I leave it off :-(

The title was supposed to be...
by azatoth (Curate) on Feb 05, 2001 at 20:40 UTC
    "Reading from ls-lrt" but it got chopped, and for some reason I can't update it. Sorry for the waste of a node here too.

    <font color="#0000ff"<Azatoth a.k.a Captain Whiplash

    Get YOUR PerlMonks Stagename here!
    A HREF="/">Want to speak like a Londoner?
Re: Reading
by azatoth (Curate) on Feb 06, 2001 at 15:21 UTC
    Just to keep you dudes updated, in the end i went for

    # snippet of my code using Net::FTP @files = reverse (map { (split (/\s+/))[8]; } `ls -lrt F*`); foreach $filename (@files[0..4]) { COPY_FILES; } sub COPY_FILES { $ftp = Net::FTP->new("$remserv") || die "Could not connect : $!\n"; $ftp->login("$user","$pass"); $ftp->cwd("$dir"); $ftp->put("$filename"); $ftp->quit; }

    I wrote it myself, and now I know a bit more about the mega-useful map !

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (2)
As of 2023-10-03 00:22 GMT
Find Nodes?
    Voting Booth?

    No recent polls found