sort?

G

g4173c

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
 
B

Brian McCauley

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;
 
B

Brian McCauley

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;
 
P

Paul Lalli

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
 
G

Greg Bacon

: 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
 
G

g4173c

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;
}
}
 
G

g4173c

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
 
P

Paul Lalli

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?
 
J

Joe Smith

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top