kep*_*aro 5 perl code-analysis
你有时会听到它说Perl可能有6种不同的方法来解决同样的问题.优秀的Perl开发人员通常有充分的理由在各种可能的实现方法之间做出选择.
所以Perl的一个例子问题:
一个简单的脚本,递归地遍历目录结构,查找最近修改过的文件(在某个特定日期之后,这将是可变的).将结果保存到文件.
问题,对于Perl开发人员:您最好的方法是什么?
pjf*_*pjf 17
这听起来像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";
Run Code Online (Sandbox Code Playgroud)
Phi*_*lds 15
问题主要由标准库解决,使用它们.
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";
}
}
Run Code Online (Sandbox Code Playgroud)
没有六种方法可以做到这一点,有旧方式和新方法.旧的方法是使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
其他人提到了File :: Find,这就是我要去的方式,但是你要求一个迭代器,File :: Find不是(也不是File :: Find :: Rule).您可能希望查看File :: Next或File :: Find :: Object,它们具有迭代接口.Mark Jason Dominus在高阶Perl的 4.2.2章中继续构建自己的.