Получение списка файлов, отсортированных по дате модификации в Perl


Я пытаюсь получить список файлов, отсортированных по дате изменения. Я изменил пример программы из сортировка файлов каталогов и списков по дате и времени и попытался запустить его.

sub get_sorted_files {
    my $path = shift;
    opendir my($dir), $path or die "can't opendir $path: $!";
    my %hash = map {$_ => (stat($_))[9]}
               map  { "$dir$_" }
               grep { m/.*/i }
               readdir $dir;
    closedir $dir;
    return %hash;
}

my %files = get_sorted_files(".");
foreach my $keys (sort{$files{$a} <=> $files{$b}} keys %files) {
    print "$keyst", scalar localtime($files{$keys}), "n";
}

Я запускаю это на моей 32-разрядной машине Windows XP, используя Strawberry Perl версии 5.12.1.0.

Список каталогов в Windows выглядит следующим образом:

текст Alt

Вывод:

текст Alt

Вывод не имеет для меня особого смысла. Что такое что-то не так с этим фрагментом кода, и как именно цикл foreach сортирует список файлов?

4 4

4 ответа:

В get_sorted_files, $dir это глобус, а не имя каталога. Возможно, вы имели в виду $path?

my %hash = map {$_ => (stat($_))[9]}
           map  { "$path/$_" }              # $path, not $dir
           grep { m/.*/i }
           readdir $dir;

С этим кодом по меньшей мере 2 проблемы. Вот лучшая версия:

use strict;
use warnings; # I bet you weren't using this, because it produced a lot

sub get_sorted_files {
   my $path = shift;
   opendir my($dir), $path or die "can't opendir $path: $!";
   my %hash = map {$_ => (stat($_))[9] || undef} # avoid empty list
           map  { "$path$_" }
           readdir $dir;
   closedir $dir;
   return %hash;
}

my %files = get_sorted_files("./");
foreach my $key (sort{$files{$a} <=> $files{$b}} keys %files) {
   print "$key\t", scalar localtime($files{$key}), "\n";
}
Во-первых, вы переименовали $dir в исходном коде в $path, но не изменили его в строке map. Ваш $dir является дескриптором каталога; именно там находится глобус (0x...) исходит от него. Во-вторых, все даты модификации читаются "Wed Dec 31 16: 00: 00 1969", потому что вы передавали неправильное имя пути к stat. (stat($_))[9] возвращал пустой список (потому что вы искали файл типа GLOB(0x3f9b38)status.txt вместо этого правильного пути) и поэтому хэш фактически оказался содержащим имена файлов как ключи, так и значения. Первое имя файла было ключом, второе-его значением, третье-следующим ключом и так далее. localtime преобразовывал имя файла в число (получая 0), а затем преобразовывал время эпохи 0 (1-Jan-1970 0:00:00 UTC) в ваш часовой пояс. В-третьих, он ожидает, что $path закончится разделителем каталогов, а вы передавали ".". Вам нужно будет пройти "./", или еще лучше, исправить это так, чтобы функция добавляет разделитель, если это необходимо. В-четвертых, grep больше ничего не делал и должен быть удален. (В исходном коде он выбирал только определенные имена файлов, но вы изменили шаблон, чтобы соответствовать чему-либо.)

Что касается того, как он сортирует имена файлов: get_sorted_files возвращает список имен путей и времени модификации, которые вы сохраняете в хэше %files. keys %files возвращает список ключей (имена файлов) и сортирует их по числовому сравнению соответствующего значения (имя файла). время модификации).

Используйте функцию Perl S sort. Это быстрее, и вы получите то, что хотите, без гашиша.

Размер файла, затем возраст файла:

@с = сортировка {-ы $а -с $б || -м $Б -М $С} @а;

Зная вышесказанное, мы можем сказать примерно следующее:
sub get_sorted_files {
   my $path = shift;
   opendir my($dirh), $path or die "can't opendir $path: $!";
   my @flist = sort {  -M $a <=> -M $b } # Sort by modification time
               map  { "$path/$_" } # We need full paths for sorting
               readdir $dirh;
   closedir $dirh;
   return @flist;
}

Для действительно больших каталогов вы можете обнаружить, что Perl значительно медленнее, чем использование собственных инструментов для сортировки. Например, на моей машине, в огромном каталоге (341k файлов), это занимает около 1,5 минут:

my $mostrecent = `/bin/ls --full-time -lta $dir | head -1 2>/dev/null`;
Но код в приведенном выше решении (с использованием opendir и sort -M) занимает от 30 до 45 секунд больше. Мало того, что это значительно быстрее, вы также можете избежать Perl хранения всего массива в памяти, что само по себе может быть выигрышем.

Обратите внимание, что вышеизложенное относится к довольно высококлассной системе Linux blade, поэтому YMMV на компьютер / ОС...