Как я могу читать файлы в каталоге в отсортированном порядке?

Когда я читаю каталог в Perl с помощью opendir, readdir и closedir, функция readdir, кажется, не читает файлы в каком-либо определенном порядке (это я могу сказать).

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

1224161460
1228324260
1229698140

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

Когда я использую readdir, первым он читает 1228324260, который является средним. Я знаю, что могу поместить содержимое каталога в массив и отсортировать массив, но есть ли опция, которую я могу передать readdir для чтения в отсортированном порядке? Или, может быть, более элегантный способ добиться этого, чем помещать все в массив и сортировать массив? Вероятно, для этого тоже есть модули, но установить модули в нашей среде сложно, поэтому, если это не встроенный модуль, я бы предпочел не использовать модули...

Спасибо!

EDIT В соответствии с просьбой я публикую код, который я использую:

opendir( my $data_dh, $data_dir ) or die "Cannot open $data_dir\n";
while ( my $name = readdir($data_dh) ) {
    next if ( $name eq '.' or $name eq '..' );
    my $full_path = "${data_dir}/${name}";
    next unless ( -d $full_path );
    process_dir($full_path);
}
closedir($data_dh);

person BrianH    schedule 12.01.2009    source источник
comment
Я использую perl 5.8.2 на компьютере с AIX, если это имеет значение.   -  person BrianH    schedule 12.01.2009
comment
Я знаю, что это простой скрипт, но можете ли вы опубликовать свой код, чтобы нам не пришлось его воспроизводить?   -  person Bill the Lizard    schedule 12.01.2009
comment
Знак & перед process_dir необязателен и в некоторых случаях может быть опасен. Я бы посоветовал вам удалить его.   -  person Leon Timmermans    schedule 12.01.2009
comment
Вы не должны использовать метод() || умереть; оператор «или» - это то, что вам нужно, когда вы связываете вызовы методов вместе. Например, opendir() или die Не удается открыть   -  person Robert P    schedule 13.01.2009
comment
Спасибо Роберт - я начну использовать или вместо   -  person BrianH    schedule 13.01.2009


Ответы (3)


readdir можно вызывать в контексте массива, поэтому просто сделайте следующее:

opendir( my $data_dh, $data_dir) or die "Cannot open $data_dir\n";
my @files = sort { $a <=> $b } readdir($data_dh);
while ( my $name = shift @files ) {
...
person Andrew Barnett    schedule 12.01.2009
comment
Вам не нужен блок { $a ‹=› $b }; здесь достаточно значения по умолчанию. - person Svante; 12.01.2009
comment
НЕТ, значения по умолчанию НЕ достаточно. По умолчанию сортировка лексическая, а не числовая. Временные метки, предшествующие 2001-09-09T01:46:40, будут сортироваться как более поздние, чем большинство временных меток после него, потому что они преодолели барьер в 1 000 000 000 секунд. - person Leon Timmermans; 12.01.2009
comment
На самом деле, opendir здесь излишество. Лучше всего было бы использовать glob()... никаких дескрипторов каталогов, о которых можно было бы беспокоиться. - person Robert P; 13.01.2009
comment
@RobertP С glob вы получите полные имена (если только $data_dir не окажется корневым). Это означает один дополнительный вызов basename или chdir. Который последний может быть проблемой во времени. - person Pavel; 28.01.2017
comment
Но тогда readdir тоже может быть плохо, то есть для файловых тестов -X, хотя вы теряете пути. - person Pavel; 28.01.2017

Вы можете попробовать это с небольшим количеством магии Glob, кажется, что glob работает отсортированным образом, так что это:

#  Glob in scalar context iterates the result set internally
while( defined( my $dir = glob($dir . '/*' ) ) ){ 
     print $dir, "\n";
    # $dir is fed ordered and with full names. 
}

or

# Glob in list context returns all results. 
for( glob($dir.'/*' ) ){ 
  print $dir , "\n";
  # also ordered. 
}

должно сработать. Просто будьте осторожны с glob, потому что это:

 for(0..20){ 
   printf "%30s|%30s\n", glob($dir.'/*' ), glob($dir.'/*' );
 }

делает что-то полуволшебное и печатает содержимое каталога дважды в каждой строке. то есть:

/foo/bar/a  |  /foo/bar/a
/foo/bar/b  |  /foo/bar/b
/foo/bar/c  |  /foo/bar/c
/foo/bar/d  |  /foo/bar/d
person Kent Fredric    schedule 12.01.2009
comment
Это, вероятно, лучший способ сделать это. opendir здесь действительно перебор. - person Robert P; 13.01.2009

Просто добавьте sort перед любым оператором списка, который вы хотите переупорядочить. Вам также не нужно хранить результаты в массиве. Вы можете использовать foreach:

opendir my($dh), $dirname or die "Could not open directory [$dirname]: $!";

foreach my $file ( sort { $a <=> $b } readdir $dh )
    {
    ...
    }
person brian d foy    schedule 12.01.2009
comment
++ для упрощения, но я думаю, что кто-то сломал вашу разметку кода (блок сортировки). - person converter42; 12.01.2009
comment
Ах, да, пре-блокам не нравятся угловые скобки, хотя в превью это выглядит красиво. - person brian d foy; 13.01.2009