Pergunta

Às vezes você ouvi-lo dito sobre Perl que pode haver 6 maneiras diferentes de abordar o mesmo problema. desenvolvedores boa Perl geralmente têm idéias bem fundamentadas para fazer escolhas entre os vários métodos possíveis de implementação.

Assim, um problema exemplo Perl:

Um script simples que de forma recursiva itera através de uma estrutura de diretórios, à procura de arquivos que foram modificados recentemente (depois de uma determinada data, o que seria variável). Salvar os resultados em um arquivo.

A questão, para os desenvolvedores Perl:? Qual é a sua melhor maneira de alcançar este objetivo

Foi útil?

Solução

Isso soa como um trabalho para File :: Procurar :: Regra :

#!/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";

Outras dicas

Quando o problema é resolvido, principalmente por bibliotecas padrão usá-los.

Arquivo :: Find, neste caso, funciona muito bem.

Pode haver muitas maneiras de fazer as coisas em perl, mas onde existe uma biblioteca muito padrão para fazer algo, deve ser utilizada a menos que tenha problemas de seu próprio.

#!/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";
  }
}

Não há seis maneiras de fazer isso, não é a maneira de idade, e a nova forma. A velha maneira é com File :: Find, e você já tem um par de exemplos disso. File :: Find tem uma interface de retorno de chamada horrível, foi legal há 20 anos, mas já evoluiu desde então.

Aqui está um programa que eu uso para limpar o cruft em um dos meus servidores de produção da vida real (levemente alterada). Ele usa File :: Find :: regra, em vez de File :: Find. File :: Pesquisar :: regra tem uma interface declarativa agradável que lê facilmente.

Randal Schwartz também escreveu File :: Finder, como um wrapper sobre File :: Find. É muito bom, mas não realmente decolou.

#! /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 :: Encontrar é o caminho certo para resolver este problema. Não há nenhum uso em reimplementar coisas que já existe em outros módulos, mas reimplementar algo que está em um módulo padrão realmente deve ser desencorajado.

Outros mencionaram File :: Find, que é a maneira que eu iria, mas você pediu um iterador, que File :: Find não é (nem é File :: Procurar :: Rule). Você pode querer olhar em File :: Próxima ou File :: Procurar :: Object , que têm uma interface iterativos. Mark Jason Dominus vai sobre a construção de sua própria no capítulo 4.2.2 do ordem superior Perl .

Meu método preferido é usar o módulo File :: Find assim:

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
}

Não é o meu File :: Localizador , como já mencionado, mas há também meu iterador-as-a-hash amarrado solução de como encontrar arquivos incremental (Linux Magazine) .

Eu escrevi File :: Procurar :: Closures como um conjunto de fechamentos que você pode usar com Arquivo :: Find para que você não tem que escrever o seu próprio. Há um par de funções mtime que deve lidar com

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->();

Você não realmente precisa usar o módulo porque eu principalmente concebido como uma maneira que você pode olhar para o código e roubar as peças que você queria. Neste caso, é um pouco mais complicado, porque todas as sub-rotinas que lidam com status dependem de um segundo sub-rotina. Você vai rapidamente ter uma idéia do código embora.

Boa sorte,

Usando módulos padrão é realmente uma boa idéia, mas por interesse aqui está a minha volta para abordagem básica sem utilizar módulos externos. Eu sei sintaxe do código aqui pode não ser o copo de todos de chá.

Pode ser melhorada para usar menos memória via proporcionando um acesso iterador (lista de entrada pode estar temporariamente em espera, uma vez que atinge um determinado tamanho) e verificação condicional pode ser expandida através de retorno de chamada ref.

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");

eu escrever uma sub-rotina que lê um diretório com readdir, joga fora o "" e ".." diretórios, recurses se encontrar um novo diretório, e examina os arquivos para o que eu estou procurando (no seu caso, você vai querer usar utime ou stat). Ao tempo da recursividade é feito, cada arquivo deve ter sido examinada.

Eu acho que todas as funções que você precisa para este script são brevemente descritos aqui: http://www.cs.cf.ac.uk/Dave/ PERL / node70.html

A semântica de entrada e saída são um exercício bastante trivial que eu vou deixar para você.

Estou riskying para se downvoted, mas IMHO 'ls' (com parâmetros apropriados) de comando faz isso de uma maneira melhor performance conhecido. Neste caso, ele pode ser bastante boa solução para tubos 'ls' a partir do código perl através de shell, retornando resultados para uma matriz ou hash.

Edit: também poderia ser 'descoberta' usado, conforme proposto nos comentários.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top