【问题标题】:Perl directories, file sort, print list with size and datePerl 目录、文件排序、带有大小和日期的打印列表
【发布时间】:2013-10-11 12:11:01
【问题描述】:

我的任务是:

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

【问题讨论】:

  • 您说“我无法遍历名称并获取每个统计信息。”为什么你不能这样做?

标签: perl file sorting


【解决方案1】:

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

  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],
    };
}

【讨论】:

  • 你能解释一下这条线吗?我不知道 qwestion 标记是什么意思。我的 $sb = $sortby eq '日期' ? 'mtime' : '路径';
【解决方案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 生成一个包含 &lt;file_name, modified_time&gt; 形式的条目的数组。然后,您可以按修改时间对该列表进行排序,并可以使用最终映射(即第一个映射)去除任何不需要的属性。在这个例子中,我没有去掉任何东西,但我希望你明白,理论上你可以在这个构建的结构中拥有其他属性,例如文件大小。

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

【讨论】:

    【解决方案3】:

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

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

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

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

    我还使用 Pod::Usage 根据我的POD documentation 打印消息。 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
    

    【讨论】:

      猜你喜欢
      • 2016-03-30
      • 2014-04-25
      • 1970-01-01
      • 1970-01-01
      • 2023-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多