sort?

Discussion in 'Perl Misc' started by g4173c@motorola.com, May 24, 2007.

  1. Guest

    Greeting:

    I have the following code to sort an array of dirs/files:

    @sorted = sort { -M $b <=> -M $a } @logfile;
    foreach $lastfilemod (@sorted) {
    $dir = (dirname $lastfilemod);
    if (! exists $srt{$dir} ) {
    push(@srtlist, $lastfilemod);
    $srt{$dir} = 1;
    }
    }

    The @logfile looks like:

    /dir/dir/file1
    /dir/dir/file2
    /dir/dir2/file1
    ....

    What I was trying to do is have @sorted list the files by last
    modified and then I could print out each file last modified in one per
    directory. However the sort isn't working correctly and I'm not
    getting the last file modified for some directories. Could anyone
    point out what I'm doing wrong?

    Thanks in advance for any help!

    Tom
    , May 24, 2007
    #1
    1. Advertising

  2. On May 24, 6:02 pm, wrote:
    > Greeting:
    >
    > I have the following code to sort an array of dirs/files:
    >
    > @sorted = sort { -M $b <=> -M $a } @logfile;
    > foreach $lastfilemod (@sorted) {
    > $dir = (dirname $lastfilemod);
    > if (! exists $srt{$dir} ) {
    > push(@srtlist, $lastfilemod);
    > $srt{$dir} = 1;
    > }
    > }
    >
    > The @logfile looks like:
    >
    > /dir/dir/file1
    > /dir/dir/file2
    > /dir/dir2/file1


    Have you checked that the values of the entries in @logfile are
    actually the _exact_ names of files that exist and that you have
    permission to read the attributes of? Are you sure that, for example,
    you don't have newline characters appended?

    I'd to a transform and check -M worked.

    my @sorted =
    map { $_->[0] }
    sort { $b->[1] <=> $a->[1] }
    @logfile;


    > ...
    >
    > What I was trying to do is have @sorted list the files by last
    > modified and then I could print out each file last modified in one per
    > directory. However the sort isn't working correctly and I'm not
    > getting the last file modified for some directories. Could anyone
    > point out what I'm doing wrong?
    >
    > Thanks in advance for any help!
    >
    > Tom
    Brian McCauley, May 24, 2007
    #2
    1. Advertising

  3. On May 24, 6:32 pm, Brian McCauley <> wrote:
    > On May 24, 6:02 pm, wrote:
    >
    >
    >
    > > Greeting:

    >
    > > I have the following code to sort an array of dirs/files:

    >
    > > @sorted = sort { -M $b <=> -M $a } @logfile;
    > > foreach $lastfilemod (@sorted) {
    > > $dir = (dirname $lastfilemod);
    > > if (! exists $srt{$dir} ) {
    > > push(@srtlist, $lastfilemod);
    > > $srt{$dir} = 1;
    > > }
    > > }

    >
    > > The @logfile looks like:

    >
    > > /dir/dir/file1
    > > /dir/dir/file2
    > > /dir/dir2/file1

    >
    > Have you checked that the values of the entries in @logfile are
    > actually the _exact_ names of files that exist and that you have
    > permission to read the attributes of? Are you sure that, for example,
    > you don't have newline characters appended?
    >
    > I'd to a transform and check -M worked.
    >
    > my @sorted =
    > map { $_->[0] }
    > sort { $b->[1] <=> $a->[1] }
    > @logfile;


    Opps hit send before I finished typing...

    # Untested
    my @sorted =
    map { $_->[0] }
    sort { $b->[1] <=> $a->[1] }
    map {
    my $mod = -M;
    if ( defined $mod ) {
    [ $_, $mod ];
    else {
    warn "$_: $!\n";
    ();
    }
    } @logfile;
    Brian McCauley, May 24, 2007
    #3
  4. Paul Lalli Guest

    On May 24, 1:02 pm, wrote:
    > Greeting:
    >
    > I have the following code to sort an array of dirs/files:
    >
    > @sorted = sort { -M $b <=> -M $a } @logfile;
    > foreach $lastfilemod (@sorted) {
    > $dir = (dirname $lastfilemod);
    > if (! exists $srt{$dir} ) {
    > push(@srtlist, $lastfilemod);
    > $srt{$dir} = 1;
    > }
    > }
    >
    > The @logfile looks like:
    >
    > /dir/dir/file1
    > /dir/dir/file2
    > /dir/dir2/file1
    > ...
    >
    > What I was trying to do is have @sorted list the files by last
    > modified and then I could print out each file last modified in one per
    > directory. However the sort isn't working correctly and I'm not
    > getting the last file modified for some directories. Could anyone
    > point out what I'm doing wrong?


    I think you have your sort backwards, for one. -M returns the number
    of days since the file was modified. You're sorting the resutls of -M
    in descending numerical order. That is, largest first. So the file
    with the LARGEST number of days since it was modified comes first.
    The file with the largest number of days since modification is the
    least recently modified, not most recently.

    Once you get this working, I also rather strongly suggest you not use
    the file-comparison operators in a sort subroutine. It's very
    ineffient, as you're doing a stat() call on every file in your list
    multiple times. Instead, use a Schwartzian transform:

    my @sorted = map { $_->[0] }
    sort { $a->[1] <=> $b->[1] }
    map { [ $_, -M $_ ] } @logfile;

    Paul Lalli
    Paul Lalli, May 24, 2007
    #4
  5. Greg Bacon Guest

    In article <>,
    <> wrote:

    : I have the following code to sort an array of dirs/files:
    :
    : @sorted = sort { -M $b <=> -M $a } @logfile;
    : foreach $lastfilemod (@sorted) {
    : $dir = (dirname $lastfilemod);
    : if (! exists $srt{$dir} ) {
    : push(@srtlist, $lastfilemod);
    : $srt{$dir} = 1;
    : }
    : }
    :
    : The @logfile looks like:
    :
    : /dir/dir/file1
    : /dir/dir/file2
    : /dir/dir2/file1
    : ...
    :
    : What I was trying to do is have @sorted list the files by last
    : modified and then I could print out each file last modified in one per
    : directory. However the sort isn't working correctly and I'm not
    : getting the last file modified for some directories. Could anyone
    : point out what I'm doing wrong?

    For each file, remember it if it was modified more recently than the
    current frontrunner for the file's parent directory:

    $ cat try
    #! /usr/bin/perl

    use warnings;
    use strict;

    use File::Basename;

    sub filename { $_[0]->[0] }
    sub modified { $_[0]->[1] }

    my %last;

    for (`find /tmp -type f 2>/dev/null`) {
    chomp;

    my $dir = dirname $_;
    my $mod = -M $_;

    $last{$dir} = [ basename($_), $mod ]
    if !$last{$dir} || $mod > modified $last{$dir};
    }

    foreach my $dir (sort keys %last) {
    print "$dir: ", filename($last{$dir}), "\n";
    }

    $ ./try
    /tmp: sh-thd-1172576387
    /tmp/exportfig: restorefig.m
    /tmp/shared: .Xdefaults
    /tmp/split-sequence: split-sequence.lisp

    Hope this helps,
    Greg
    --
    The idea that the vote of a people, no matter how nearly unanimous, makes or
    creates or determines what is right or just becomes as absurd and
    unacceptable as the idea that right and justice are simply whatever a king
    says they are. -- Robert Welch
    Greg Bacon, May 24, 2007
    #5
  6. Guest

    Hi All:

    Thanks for all the responces! I found that -M doesn't give the
    granularity I needed. Some of the files were only seconds away for the
    other. I changed to the following:

    use File::stat;
    @sorted = sort { (stat($b)->mtime) <=> (stat($a)->mtime) }
    @logfile;
    foreach $lastfilemod (@sorted) {
    $dir = (dirname $lastfilemod);
    if (! exists $srt{$dir} ) {
    push(@srtlist, $lastfilemod);
    $srt{$dir} = 1;
    }
    }
    , May 24, 2007
    #6
  7. Guest

    Hi All:

    Thanks for all the responces! I found that the -M doesn't give the
    granularity I need. Some of the files were only seconds apart. I
    changed to the following:

    use File::stat;
    @sorted = sort { (stat($b)->mtime) <=> (stat($a)->mtime) }
    @logfile;
    foreach $lastfilemod (@sorted) {
    $dir = (dirname $lastfilemod);
    if (! exists $srt{$dir} ) {
    push(@srtlist, $lastfilemod);
    $srt{$dir} = 1;
    }
    }

    This got just what I was looking for. Thanks again for all the replys!

    Tom
    , May 24, 2007
    #7
  8. Paul Lalli Guest

    On May 24, 2:28 pm, wrote:
    > Thanks for all the responces! I found that -M doesn't give the
    > granularity I needed.


    I'm sorry, what?!

    [14:43:46] ~/tmp $ touch file.txt
    [14:43:52] ~/tmp $ perl -le'print -M "file.txt"'
    0.000115740740740741

    Exactly how much more granularity do you need?!

    Paul Lalli


    Some of the files were only seconds away for the
    > other. I changed to the following:
    >
    > use File::stat;
    > @sorted = sort { (stat($b)->mtime) <=> (stat($a)->mtime) }
    > @logfile;


    The reason this works and your other one didn't is because you're now
    comparing the last modified times as seconds sine the epoch, not days
    since last modified. So NOW, your descending numerical sort is
    correct. The file with the greatest seconds since the epoch is in
    fact the most recently modified. Compare and contrast with the file
    with the greatest days since last modified. *

    If you had just swapped $a and $b in your original, it would have been
    fine.

    Paul Lalli

    * Here's an example. Presuming we're running at 14:55:00 on May 24,
    2007....
    file1 - last modified Jan 1, 1970 00:00:00
    file2 - last modified May 23, 2007, 14:55:00
    file3 - last modified May 24, 2007, 14:55:00

    -M file1 returns 13657.7849421296
    -M file2 returns 1
    -M file3 returns 0

    stat(file1)->mtime returns 0
    stat(file2)->mtime returns 1179946500
    stat(file3)->mtime returns 1180032900

    Do you see now what you did wrong the first time?
    Paul Lalli, May 24, 2007
    #8
  9. Joe Smith Guest

    wrote:
    > Hi All:
    >
    > Thanks for all the responces! I found that the -M doesn't give the
    > granularity I need. Some of the files were only seconds apart.


    So what, pray tell, do you think -M actually returns?
    It is _NOT_ the integer number of days of age.

    Proof that -M has all the granularity you need:

    linux% foreach n (0 1 2 3)
    foreach? touch -d "2-Jan-1970 12:00:0$n" $n
    foreach? end
    linux% ls -l ?
    -rw-r--r-- 1 jms jms 0 Jan 2 1970 0
    -rw-r--r-- 1 jms jms 0 Jan 2 1970 1
    -rw-r--r-- 1 jms jms 0 Jan 2 1970 2
    -rw-r--r-- 1 jms jms 0 Jan 2 1970 3
    linux% perl -le 'print "file $_ has -M = ", -M $_ for @ARGV' ?
    file 0 has -M = 13661.9331481481
    file 1 has -M = 13661.9331365741
    file 2 has -M = 13661.933125
    file 3 has -M = 13661.9331134259
    1/(24*60*60) = 0.00001157407

    Where did you come up with the idea that -M is not suitable?
    -Joe
    Joe Smith, May 30, 2007
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. nobody
    Replies:
    0
    Views:
    521
    nobody
    Jun 1, 2004
  2. JerryJ
    Replies:
    11
    Views:
    1,375
    Dave Moore
    Apr 28, 2004
  3. John Black
    Replies:
    6
    Views:
    2,038
    John Harrison
    May 28, 2004
  4. Angus Comber
    Replies:
    7
    Views:
    1,140
    Richard Heathfield
    Feb 5, 2004
  5. Navin
    Replies:
    1
    Views:
    659
    Ken Schaefer
    Sep 9, 2003
Loading...

Share This Page