你有时会听到它说Perl可能有6种不同的方法来解决同样的问题.优秀的Perl开发人员通常有充分的理由在各种可能的实现方法之间做出选择.
所以Perl的一个例子问题:
一个简单的脚本,递归地遍历目录结构,查找最近修改过的文件(在某个特定日期之后,这将是可变的).将结果保存到文件.
问题,对于Perl开发人员:您最好的方法是什么?
这听起来像File :: Find :: Rule的工作:
#!/usr/bin/perl use strict; use warnings; use autodie; # Causes built-ins like open to succeed or die. # You can 'use Fatal qw(open)' if autodie is not installed. use File::Find::Rule; use Getopt::Std; use constant SECONDS_IN_DAY => 24 * 60 * 60; our %option = ( m => 1, # -m switch: days ago modified, defaults to 1 o => undef, # -o switch: output file, defaults to STDOUT ); getopts('m:o:', \%option); # If we haven't been given directories to search, default to the # current working directory. if (not @ARGV) { @ARGV = ( '.' ); } print STDERR "Finding files changed in the last $option{m} day(s)\n"; # Convert our time in days into a timestamp in seconds from the epoch. my $last_modified_timestamp = time() - SECONDS_IN_DAY * $option{m}; # Now find all the regular files, which have been modified in the last # $option{m} days, looking in all the locations specified in # @ARGV (our remaining command line arguments). my @files = File::Find::Rule->file() ->mtime(">= $last_modified_timestamp") ->in(@ARGV); # $out_fh will store the filehandle where we send the file list. # It defaults to STDOUT. my $out_fh = \*STDOUT; if ($option{o}) { open($out_fh, '>', $option{o}); } # Print our results. print {$out_fh} join("\n", @files), "\n";
问题主要由标准库解决,使用它们.
File :: Find在这种情况下效果很好.
在perl中可能有很多方法可以做,但是如果存在一个非常标准的库来执行某些操作,除非它有自己的问题,否则应该使用它.
#!/usr/bin/perl use strict; use File::Find(); File::Find::find( {wanted => \&wanted}, "."); sub wanted { my (@stat); my ($time) = time(); my ($days) = 5 * 60 * 60 * 24; @stat = stat($_); if (($time - $stat[9]) >= $days) { print "$_ \n"; } }
没有六种方法可以做到这一点,有旧方式和新方法.旧的方法是使用File :: Find,你已经有了几个例子.File :: Find有一个非常糟糕的回调接口,它在20年前很酷,但从那时起我们就开始了.
这是一个真实的生活(轻微修改)程序,我用它来清除我的一个生产服务器上的残骸.它使用File :: Find :: Rule,而不是File :: Find.File :: Find :: Rule有一个很好的声明性接口,可以轻松读取.
Randal Schwartz还编写了File :: Finder,作为File :: Find的包装器.这很不错但它还没有真正起飞.
#! /usr/bin/perl -w # delete temp files on agr1 use strict; use File::Find::Rule; use File::Path 'rmtree'; for my $file ( File::Find::Rule->new ->mtime( '<' . days_ago(2) ) ->name( qr/^CGItemp\d+$/ ) ->file() ->in('/tmp'), File::Find::Rule->new ->mtime( '<' . days_ago(20) ) ->name( qr/^listener-\d{4}-\d{2}-\d{2}-\d{4}.log$/ ) ->file() ->maxdepth(1) ->in('/usr/oracle/ora81/network/log'), File::Find::Rule->new ->mtime( '<' . days_ago(10) ) ->name( qr/^batch[_-]\d{8}-\d{4}\.run\.txt$/ ) ->file() ->maxdepth(1) ->in('/var/log/req'), File::Find::Rule->new ->mtime( '<' . days_ago(20) ) ->or( File::Find::Rule->name( qr/^remove-\d{8}-\d{6}\.txt$/ ), File::Find::Rule->name( qr/^insert-tp-\d{8}-\d{4}\.log$/ ), ) ->file() ->maxdepth(1) ->in('/home/agdata/import/logs'), File::Find::Rule->new ->mtime( '<' . days_ago(90) ) ->or( File::Find::Rule->name( qr/^\d{8}-\d{6}\.txt$/ ), File::Find::Rule->name( qr/^\d{8}-\d{4}\.report\.txt$/ ), ) ->file() ->maxdepth(1) ->in('/home/agdata/redo/log'), ) { if (unlink $file) { print "ok $file\n"; } else { print "fail $file: $!\n"; } } { my $now; sub days_ago { # days as number of seconds $now ||= time; return $now - (86400 * shift); } }
File :: Find是解决此问题的正确方法.重新实现其他模块中已经存在的东西是没有用的,但是应该不鼓励重新实现标准模块中的某些东西.
其他人提到了File :: Find,这就是我要去的方式,但是你要求一个迭代器,File :: Find不是(也不是File :: Find :: Rule).您可能希望查看File :: Next或File :: Find :: Object,它们具有迭代接口.Mark Jason Dominus在高阶Perl的 4.2.2章中继续构建自己的.