2013-12-16 73 views
1

我需要做的是编写一个脚本,它将读取目录列表 ,然后对它们进行排序,最后一个目录将被“关闭”。在ASCII码上按日期对PERL数组进行排序

my $ last_one = pop @sorted;

然后去年目录将会被删除 - 用系统(“RM-RF $ last_one)或 remove_tree($ last_one)

1 #!/usr/bin/perl 
2 use strict; 
3 use warnings; 
4 
5 my $dir_to_process = "/production/log/fo/archive/"; 
6 opendir DH, $dir_to_process or die "Sorry, this is not going to work out $!"; 
7 
8 while (my $name = readdir DH) { 
9  next if $name =~ /^\./; 
10  push(my @unsorted,$name) ; 
11  my @sorted_dir = sort @unsorted; 
12  foreach my $sorted (@sorted_dir) { 
13  print "$sorted\n"; 
14  sleep 1 ; 
15  } 
16 
17 } 

但是我有很多的麻烦分类目录。 - 。它们是由这种格式写 这是实际的输出

2013Nov12 
2013Sep14 
2013Jul15 
2013Jan20 
2013Sep11 
2013May31 
2013Jul04 
2012Dec09 
2013Oct12 
2013Oct09 
2012Dec27 
2013Nov28 
2013Mar24 
2013Jun06 
2013Jun25 
+0

你在标题中说“通过ASCII排序”,但我假设你真正想要的是按日期排序(即最早到最新或反之亦然)? – ThisSuitIsBlackNot

+1

如果你只是想找到最古老的,排序是没有意义的。 – ikegami

+0

问题是,它是按ascii排序,而不是按日期排序 - 是的,我需要的是最古老的日期目录。 – capser

回答

2

排序()接受代码块,你可以定义自己的排序算法,只需将您的目录名称为timestamp,你应该去这。将是卑鄙的像
sort { date2stamp($a) <=> date2stamp($b) } @unsorted
而内部date2stamp子使用POSIX :: mktime()创建日期字符串的正确的时间戳。

3

您需要一个函数将您的目录名称转换为日期(将日期信息拆分为您可以排序的内容:年,月,日)。

下面是一个例子

sub parsedate { 
    my $name = shift; 
    my %months = ('Jan'=> 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4, 
    'May'=> 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 
    'Sep'=> 9, 'Oct' => 10, 'Nov' => 11, 'Dec' => 12); 

    my ($y,$m,$d); 
    $name =~ m/^(\d{4})(\w{3})(\d{2})$/ 
    and ($y,$m,$d) = ($1,$months{$2},$3) 
    or die "file name $name doesn't match"; 

    return sprintf("%04d%02d%02d",$y,$m,$d); 
} 

现在你可以使用你的新的子parsedate只是对它们进行排序。

my @sorted_dir = sort {parsedate($a) <=> parsedate($b)} @unsorted; 
2

转化月份名称为数字,然后做了一些出来的年月日 的那么数数字 的“使用Schwartzian变换”排序是用来将数字附加到文件名

#!/usr/bin/perl 
# 
# 
use warnings; 
use strict; 

my %monthval=qw(Jan 01 Feb 02 Mar 03 Apr 04 May 05 Jun 06 Jul 07 Aug 08 Sep 09 O 
ct 10 Nov 11 Dec 12); 


my @in=qw(2013Nov12 
2013Sep14 
2013Jul15 
2013Jan20 
2013Sep11 
2013May31 
2013Jul04 
2012Dec09 
2013Oct12 
2013Oct09 
2012Dec27 
2013Nov28 
2013Mar24 
2013Jun06 
2013Jun25); 

my @sorted = map{$_->[0]} sort { $a->[1] <=> $b->[1]} map { my ($y,$m,$d)=/(\d{ 
4})(\w{3})(\d{2})/; [$_,"$y".$monthval{$m}."$d"]} @in; 

foreach my $name (@sorted) { 
    print "$name\n"; 
} 
5

假设存在一个函数convert_date,它将日期转换为YYYYMMDD格式。如果你有一个,一个简单的字符串比较会发现最古老的。

my ($oldest) = 
    sort { convert_date($a) cmp convert_date($b) } 
    @dirs; 

更快:

my ($oldest) = 
    map $_->[0], 
    sort { $a->[1] cmp $b->[1] } 
    map [ $_, convert_date($_) ], 
    @dirs; 

最快:

my ($oldest) = 
    map substr($_, 8), 
    sort 
    map convert_date($_) . $_, 
    @dirs; 

但是排序(O(N日志N))是寻找一种元素(O的浪费方式(N ))。

my $oldest = $dirs[0]; 
for (@dirs) { 
    $oldest = $_ if convert_date($_) lt $oldest; 
} 

更快?

use List::Util qw(minstr); 
my $oldest = substr(minstr(map { convert_date($_) . $_ } @dirs), 8); 

现在,所有剩下的就是写convert_date

use Carp qw(croak); 

my %month_num_by_en_name = (
    Jan => 1, Feb => 2, Mar => 3, Apr => 4, 
    May => 5, Jun => 6, Jul => 7, Aug => 8, 
    Sep => 9, Oct => 10, Nov => 11, Dec => 12, 
); 


sub convert_date { 
    my ($date) = @_; 

    my ($y,$m,$d) = $date =~ m/^(\d{4})(\w{3})(\d{2})\z/ 
     or croak("Invalid input"); 

    $m = $month_num_by_en_name{$m} 
     or croak("Invalid input"); 

    return sprintf("%04d%02d%02d", $y,$m,$d); 
} 

您也可以使用DateTime::Format::Strptime。这使得支持其他语言更容易。

use DateTime::Format::Strptime qw(); 

my $format = DateTime::Format::Strptime->new(
    pattern => '%Y%b%d', 
    locale => 'en_US', 
    on_error => 'croak', 
); 

sub convert_date { 
    my ($date) = @_; 
    return $format->parse_datetime($date)->strftime('%Y%m%d'); 
} 
+0

真棒解释。你介意我问你为什么使用'鲤鱼'?它是习惯性还是有收获(总是谈论OP的情景)? – foibs

+0

@foibs,因为鲤鱼提供'croak',这在这里很合适。 – ikegami

+0

对不起,我不清楚。我的意思是'croak'比'die'更合适。谢谢 – foibs