Как я могу найти самый новый файл.pl в каталоге и во всех его подкаталогах, используя Perl?

Как я могу сканировать содержимое всего каталога, включая содержимое его подкаталогов, и найти самое новое .pl файл внутри них с помощью Perl?

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

Так, например, если мой базовый каталог /home/users/cheeseconqueso/ Я хочу искать .pl файлы в этом каталоге и любом подкаталоге в этом пути, а затем сортировать .pl файлы по дате.

Конечным результатом будет массив, @pl_paths, где $pl_paths[0] было бы что-то вроде /home/users/cheeseconqueso/maybe_not_newest_directory/surely_newest_file.pl

Исходя из этого, я хочу выполнить файл, но я думаю, как только я выясню, отсортированный массив, выполняя файл в $pl_paths[0]не будет проблемой.

Есть аналогичный вопрос о SO, который я пытался изменить в соответствии со своими потребностями, но я здесь сейчас по очевидным причинам.

Код, который я использую для получения новейшего файла NAME только в одном каталоге:

opendir(my $DH, $DIR) or die "Error opening $DIR: $!";
my %files = map { $_ => (stat("$DIR/$_"))[9] } grep(! /^\.\.?$/, readdir($DH));
closedir($DH);
my @sorted_files = sort { $files{$b} <=> $files{$a} } (keys %files);
print $sorted_files[0]."\n";

3 ответа

Решение

Вы можете использовать File::Find, если вам нужен основной модуль для этого, но я бы предпочел использовать File::Find:: Rule.

Для начала мы можем найти все .pl файлы в каталоге с

use File::Find::Rule;
my @files = File::Find::Rule->file
                            ->name('*.pl')
                            ->in($directory);

Тогда давайте использовать map чтобы связать имена файлов со временем их изменения:

my @files_with_mtimes = map +{ name => $_, mtime => (stat $_)[9] }, @files;

И отсортировать их по mtime:

my @sorted_files = reverse sort { $a->{mtime} <=> $b->{mtime} } 
                @files_with_mtimes;

И оттуда, имя новейшего в $sorted_files[0]{name},

Если вы хотите найти только верхний вариант, на самом деле нет нужды делать полную сортировку, но самое хорошее решение, о котором я могу подумать, - это немного продвинутый FP, так что не беспокойтесь об этом, если он вам покажется странным:

use List::Util 'reduce';
my ($top_file) = reduce { $a->{mtime} >= $b->{mtime} ? $a : $b } 
  @files_with_mtimes;

С File::Find::Rule и преобразованием Шварца вы можете получить новейший файл с расширением.pl в поддереве, начиная с dir_path.

#!/usr/bin/env perl

use v5.12;
use strict;
use File::Find::Rule;

my @files = File::Find::Rule->file()->name( '*.pl' )->in( 'dir_path' );

# Note that (stat $_ )[ 9 ] yields last modified timestamp
@files = 
   map { $_->[ 0 ] }
   sort { $b->[ 1 ] <=> $a->[ 1 ] }
   map { [ $_, ( stat $_ )[ 9 ] ] } @files;

# Here is the newest file in path dir_path
say $files[ 0 ];

Цепочка map-sort-map является типичной идиомой: получение метки времени происходит медленно, поэтому мы делаем это только один раз для каждого файла, сохраняя каждую метку времени вместе с файлом в arrayref. Затем мы сортируем новый список с использованием метки времени (сравнивая второй элемент каждого массива) и, наконец, отбрасываем метки времени, сохраняя только имена файлов.

Используйте основной модуль File::Find.

Другие вопросы по тегам