Perl目录,文件排序,打印列表,包括大小和日期

时间:2013-10-09 22:56:28

标签: perl file sorting

我的任务是:

  1. 从命令行中读取目录,排序类型和排序顺序。
  2. 对文件名进行排序,并使用大小和日期打印出来。
  3. 这是我到目前为止所得到的。

    #!/usr/bin/perl
    
    use 5.010;
    use strict;
    use warnings;
    
    use Getopt::Long;
    my $dir = "";
    my $sortby = "";
    my $order = "";
    my $result;
    
    $result = GetOptions (
                        'dir=s'  => \$dir,                # specify derictory
                       'sortby=s'    =>  \$sortby,        # 'name' or 'date'
                       'order=s'     =>  \$order);        # 'asc'- or 'des'-ending order of sorting
    
    print "derictory = $dir, sortby  = $sortby, order = $order \n\n";
    
    
    opendir (DH, $dir)or die "couldn open dericroty: $!\n";
    my @filenames = grep ! /^\./, readdir DH;
    closedir (DH);
    
    if ($sortby eq "name") {
    
         if ($order eq "asc") {
             foreach my $name (sort {lc $a cmp lc $b} @filenames) {
             my @statinfo = stat("$dir/$name");
             print "$name\tsize= " . $statinfo[7] . ",\t last modified=" . 
             scalar(localtime($statinfo[9])) . "\n";
             }
         }
         elsif ($order eq "des") {
             foreach my $name (sort {lc $b cmp lc $a} @filenames) {
             my @statinfo = stat("$dir/$name");
             print "$name\tsize= " . $statinfo[7] . ",\t last modified=" . 
             scalar(localtime($statinfo[9])) . "\n";
             }
         }      
    }
    
    
    if ($sortby eq "date") {
        if ($order eq "asc") {
            @filenames = sort { -M "$dir/$a" <=> -M "$dir/$b" } (@filenames);   
            print  join ("\n", @filenames);
        }    
        elsif ($order eq "des") {
            @filenames = sort { -M "$dir/$b" <=> -M "$dir/$a" } (@filenames);   
            print  join ("\n", @filenames);
        }    
    }
    

    问题是如果我需要按修改日期对其进行排序,我不知道如何打印出大小和日期的文件名列表。我想我应该使用stat函数,但我不能遍历名称,并得到每个统计数据 我上面所有的基本上是我能够谷歌和放在一起。

3 个答案:

答案 0 :(得分:3)

这是思考问题的另一种方式。要点:

  1. 编写执行简单操作的小功能,并构建您的程序 将这些功能组合在一起。

  2. 如果您在方便的数据中收集所有信息 结构(在本例中,哈希列表),算法/逻辑 该计划的各个方面变得简单而自然。

  3. 为简单起见,此示例忽略选项解析,而只是接受params作为常规命令行参数。

    use strict;
    use warnings;
    
    main();
    
    sub main {
        my ($dir, $sortby, $order) = @ARGV;
    
        my @contents = read_dir($dir);
        my $sb       = $sortby eq 'date' ? 'mtime' : 'path';
        my @sorted   = sort { $a->{$sb} cmp $b->{$sb}  } @contents;
        @sorted      = reverse(@sorted) if $order eq 'des';
    
        for my $fi (@sorted){
            print $fi->{path}, ' : ', $fi->{mtime}, "\n";
        }
    }
    
    sub read_dir {
        # Takes a dir path.
        # Returns a list of file_info() hash refs.
        my $d = shift;
        opendir(my $dh, $d) or die $!;
        return map  { file_info($_) }  # Collect info.
               map  { "$d/$_" }        # Attach dir path.
               grep { ! /^\.\.?$/ }    # No dot dirs.
               readdir($dh);
    }
    
    sub file_info {
        # Takes a path to a file/dir.
        # Returns hash ref containing the path plus any stat() info you need.
        my $f = shift;
        my @s = stat($f);
        return {
            path  => $f,
            mtime => $s[9],
        };
    }
    

答案 1 :(得分:1)

如果您要按数据的某些属性进行排序,可能需要查看Schwartzian Transform。这是如何使用它按修改时间排序的基本示例:

use strict;
use warnings;

use constant MTIME_STAT_INDEX => 9;
use constant FILENAME_INDEX => 0;
use constant MTIME_INDEX => 1;

# Grab a list of files in the current folder
my $some_dir = '.';
opendir(my $dh, $some_dir) || die "can't opendir $some_dir: $!";
my @fileNames = readdir $dh;
closedir $dh;

# Use a Schwartzian transform to generate a sorted list of <file_name, mtime> tuples 
my @sortedByMtime = 
    map { $_ }
    sort { $a->[MTIME_INDEX] cmp $b->[MTIME_INDEX] } 
    map { [$_, (stat($_))[MTIME_STAT_INDEX]] } @fileNames;

# Print the file name and mtime
for my $sortedRecord (@sortedByMtime) {
    print $sortedRecord->[FILENAME_INDEX] . "\t" . $sortedRecord->[MTIME_INDEX] . "\n";
}

1;

从外到内读取变换可能会有所帮助(即从最后开始并朝着开始工作)。从文件名列表开始,使用map生成包含<file_name, modified_time>形式的条目的数组。然后,您可以按修改时间对此列表进行排序,并可以使用最终映射(即第一个映射)来去除任何不需要的属性。在这个例子中,我没有删除任何东西,但我希望你能理解你在理论上可以在这个构建的结构中有其他属性,例如文件大小。

这只是为了让您开始作为概念证明 - 我没有考虑效率,错误处理或使输出相当漂亮。

答案 2 :(得分:0)

你应该看看File::stat。该模块(Subversion附带)允许您轻松访问有关该文件的各种信息。

您还应该查看Time::Piece。此模块允许您轻松格式化日期和时间。

我也不担心有四个单独的排序例程。相反,只需按照数组标准升序对所需内容进行排序。然后,在打印之前,查看用户是否请求降序。如果用户确实请求降序,则可以使用reverse来反转已排序的数组。

我正在使用References。我存储文件名的数组不包含字符串,而是包含引用到哈希。这样,我的数组中的每个条目都包含有关我文件的四个独立信息。

我也使用Pod :: Usage根据我的POD documentation打印消息。 POD是一种相当简单的格式,用于存储有关程序的文档。用户可以使用perldoc命令显示pod:

$ perldoc prog.pl

或者,他们可以使用pod2html等命令将文档转换为HTML。这些各种Perldoc和POD命令随Perl发行版一起提供。我强烈建议您学习POD并广泛使用它。它将您的程序文档保存在您的程序中,并允许您为您的文档生成各种格式。 (文本,HTML,联机帮助,降价,维基等)。

#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);
use autodie;

# All of these are standard Perl module and come with all distributions
# or Perl

use Time::Piece;
use File::stat;
use Getopt::Long;
use Pod::Usage;
use File::Basename;

my ( $directory, $sort_order, $sort_descending, $help );

#
# Using pod2usage to print out my messages
#
GetOptions (
    "directory=s"   => \$directory,
    "sort=s"        => \$sort_order,
    "descending"    => \$sort_descending,
    "help"          => \$help,
) or pod2usage;

if ( $help ) {
    pod2usage ( -message => qq(Use command 'perldoc print_dir.pl' for complete documetation) );
}

if ( not ( defined $directory and defined $sort_order ) ) {
    pod2usage ( -message => qq(Must use parameters "directory" and "sort") );
}

if ( $sort_order ne "name"  and
     $sort_order ne "ctime" and
     $sort_order ne "size"  and
     $sort_order ne "mtime" ) {
     die qq(Sort order must be "name", "size", "ctime", or "mtime"\n);
 }

opendir ( my $dir_fh, $directory );         #Will autodie here if directory doesn't exist

my @files;
while ( my $file = readdir $dir_fh ) {
    $file = "$directory/$file";
    next if not -f $file;

    #
    # Note I'm using File::stat to get the info on the files
    #

    my $stat = stat $file or die qq(Couldn't stat file "$file"\n);
    my %file;
    $file{NAME}  = basename $file;
    $file{CTIME} = $stat->ctime;
    $file{MTIME} = $stat->mtime;
    $file{SIZE}  = $stat->size;
    #
    # I'm storing this information in a hash and pushing a Hash Reference
    #
    push @files, \%file;    #Pushing a reference to the hash
}
closedir $dir_fh;

my @sorted_files =  sort file_sort @files;

#
# I am using the fact that my hash keys and my sort options
# are very similar. One routine sorts all which ways
#
sub file_sort {
    my $sort_by = uc $sort_order;
    if ( $sort_order eq "name" ) {
        return $a->{$sort_by} cmp $b->{$sort_by};
    } else {
        return $a->{$sort_by} <=> $b->{$sort_by};
    }
}

#
# If the user wants descending order, reverse the array
#
if ( $sort_descending ) {
    @sorted_files = reverse @sorted_files;
}

#
# I'm using 'printf' to print out a nice report.
# My $format is the format of the report, and I
# can use it for the title or the body.
#
my $format = "%-20.20s  %-10d  %-11.11s  %-11.11s\n";
( my $title_format = $format ) =~ s/d/s/;
printf $title_format, "Name", "Sixe", "Mod-Time", "C-Time";
say join "  ", "=" x 20, "=" x 10, "=" x 11, "=" x 11;
for my $file ( @sorted_files ) {
    #
    # The "->" dereferences the hash
    # Note how I use Time::Piece to format my time
    #
    my $mtime = Time::Piece->new ( $file->{MTIME} );
    my $ctime = Time::Piece->new ( $file->{CTIME} );
    printf $format, $file->{NAME}, $file->{SIZE}, $mtime->ymd, $ctime->ymd;
}

#
# Here be the Plain Old Documention (POD) This is the standard
# way to document Perl programs. You can use the "perldoc" program
# to print it out, and pod2usage to print out bits and pieces.
#

=pod

=head1 NAME

print_dir.pl

=head1 SYNOPSIS

    print_dir.pl -sort [name|size|mtime|ctime] -directory $directory [ -descending ]

=head1 DESCRIPTION

This program does somee amazing wonderful stuff...

=head1 OPTIONS

=over 4

=item *

-sort

(Required) Sort order of directory parameters can be C<name>, C<size>, C<mtime>, C<ctime>

=item *

-directory

(Required) Name of the directory to print

=item *

-descending

(Optional) Sort in descending order instead of ascending order

=back

=cut