2

我的任务是:

  1. 从命令行读取目录、排序类型和排序顺序。
  2. 对文件名进行排序并用大小和日期打印出来。

这是我到目前为止得到的。

#!/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 函数,但我无法遍历名称并获取每个统计信息。
我上面所拥有的基本上就是我能够谷歌并放在一起的东西。

4

3 回答 3

3

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

  1. 编写做简单事情的小函数,并通过将这些函数组合在一起来构建您的程序。

  2. 如果您以方便的数据结构(在此示例中为哈希列表)收集所有信息,则程序的算法/逻辑方面变得简单而自然。

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

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],
    };
}
于 2013-10-10T03:26:17.657 回答
2

如果您要按数据的某些属性进行排序,您可能需要查看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>。然后,您可以按修改时间对该列表进行排序,并可以使用最终映射(即第一个映射)去除任何不需要的属性。在这个例子中,我没有去掉任何东西,但我希望你明白,理论上你可以在这个构建的结构中拥有其他属性,例如文件大小。

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

于 2013-10-10T01:35:30.113 回答
1

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

您还应该查看Time::Piece。此模块可让您轻松设置日期和时间的格式。

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

我正在使用参考。我存储文件名的数组不包含字符串,而是对哈希的引用。这样,我的数组中的每个条目都包含关于我的文件的四个单独的信息位。

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

$ perldoc prog.pl

或者,他们可以使用诸如pod2html将文档转换为 HTML 的命令。这些不同的 Perldoc 和 POD 命令随您的 Perl 发行版一起提供。我强烈建议您学习 POD 并广泛使用它。它将您的程序文档保存在您的程序中,并允许您为您的文档生成各种格式。(文本、HTML、手册页、markdown、wiki 等)。

#! /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
于 2013-10-10T03:23:43.070 回答