代码之家  ›  专栏  ›  技术社区  ›  keparo bshirley

Perl挑战-目录迭代器

  •  5
  • keparo bshirley  · 技术社区  · 17 年前

    您有时会听到关于Perl的说法,可能有6种不同的方法来解决同一个问题。优秀的Perl开发人员通常具有合理的洞察力,可以在各种可能的实现方法之间做出选择。

    下面是一个Perl问题示例:

    一个简单的脚本,递归地遍历目录结构,查找最近修改过的文件(在某个日期之后,可能是可变的)。将结果保存到文件中。

    11 回复  |  直到 14 年前
        1
  •  17
  •   Ian    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";
    
        2
  •  15
  •   Philip Reynolds    17 年前

    在这种情况下,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";
      }
    }
    
        3
  •  9
  •   dland    17 年前

    有六种方法可以做到这一点,有旧的方法,也有新的方法。旧的方法是使用File::Find,您已经有了几个这样的例子。Find有一个非常糟糕的回调接口,20年前很酷,但从那时起我们就开始前进了。

    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);
        }
    }
    
        4
  •  8
  •   Leon Timmermans    17 年前

    File::Find 是解决这个问题的正确方法。重新实现其他模块中已经存在的东西是没有用的,但是重新实现标准模块中的东西确实应该被劝阻。

        5
  •  8
  •   runrig    17 年前

    其他人提到了File::Find,这是我的方式,但您要求使用迭代器,而File::Find不是(File::Find::Rule也不是)。你可能想看看 File::Next File::Find::Object Higher Order Perl

        6
  •  4
  •   workmad3    17 年前

    use File::Find;
    find (\&checkFile, $directory_to_check_recursively);
    
    sub checkFile()
    {
       #examine each file in here. Filename is in $_ and you are chdired into it's directory
       #directory is also available in $File::Find::dir
    }
    
        8
  •  3
  •   brian d foy    17 年前

    我写 File::Find::Closures 作为一组闭包,可以与File::Find一起使用,这样就不必编写自己的闭包。有几个mtime函数应该处理

    use File::Find;
    use File::Find::Closures qw(:all);
    
    my( $wanted, $list_reporter ) = find_by_modified_after( time - 86400 );
    #my( $wanted, $list_reporter ) = find_by_modified_before( time - 86400 );
    
    File::Find::find( $wanted, @directories );
    
    my @modified = $list_reporter->();
    

    祝你好运

        9
  •  0
  •   Adi Lester    13 年前

    使用标准模块确实是一个好主意,但出于兴趣,这里是我不使用外部模块的基本方法。我知道这里的代码语法可能不是每个人都喜欢的。

    sub mfind {
        my %done;
    
        sub find {
            my $last_mod = shift;
            my $path = shift;
    
            #determine physical link if symlink
            $path = readlink($path) || $path;        
    
            #return if already processed
            return if $done{$path} > 1;
    
            #mark path as processed
            $done{$path}++;
    
            #DFS recursion 
            return grep{$_} @_
                   ? ( find($last_mod, $path), find($last_mod, @_) ) 
                    : -d $path
                       ? find($last_mod, glob("$path/*") )
                           : -f $path && (stat($path))[9] >= $last_mod 
                               ? $path : undef;
        }
    
        return find(@_);
    }
    
    print join "\n", mfind(time - 1 * 86400, "some path");
    
        10
  •  -1
  •   antik    17 年前

    我编写了一个子程序,用 readdir ,抛出“.”和“.”目录,在找到新目录时递归,并检查文件以查找我要查找的内容(在您的情况下,您需要使用 utime stat )。当递归完成时,应该已经检查了每个文件。

    http://www.cs.cf.ac.uk/Dave/PERL/node70.html

    输入和输出的语义是一个相当简单的练习,我将留给您。

        11
  •  -2
  •   Thevs    17 年前

    我冒着被否决的风险,但IMHO'ls(带有适当的参数)命令以一种最著名的性能方式完成了这项任务。在本例中,通过shell将perl代码中的“ls”传递给shell,将结果返回到数组或散列,这可能是一个非常好的解决方案。

    编辑:也可以按照评论中的建议使用“查找”。