Domanda

Talvolta si sente dire di Perl che potrebbero esserci 6 modi diversi di affrontare lo stesso problema. Gli sviluppatori di Good Perl di solito hanno intuizioni ben ragionate per fare scelte tra i vari possibili metodi di implementazione.

Quindi un problema Perl di esempio:

Un semplice script che scorre in modo ricorsivo attraverso una struttura di directory, alla ricerca di file che sono stati modificati di recente (dopo una certa data, che sarebbe variabile). Salva i risultati in un file.

La domanda, per gli sviluppatori Perl: qual è il tuo modo migliore per raggiungere questo obiettivo?

È stato utile?

Soluzione

Sembra un lavoro per File :: Trova :: Regola :

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

Altri suggerimenti

Dove il problema è risolto principalmente dalle librerie standard, usali.

File :: Trova in questo caso funziona bene.

Ci possono essere molti modi per fare le cose in perl, ma dove esiste una libreria molto standard per fare qualcosa, dovrebbe essere utilizzata a meno che non abbia problemi propri.

#!/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(

Dove il problema è risolto principalmente dalle librerie standard, usali.

File :: Trova in questo caso funziona bene.

Ci possono essere molti modi per fare le cose in perl, ma dove esiste una libreria molto standard per fare qualcosa, dovrebbe essere utilizzata a meno che non abbia problemi propri.

<*>); if (($time - $stat[9]) >= $days) { print "

Dove il problema è risolto principalmente dalle librerie standard, usali.

File :: Trova in questo caso funziona bene.

Ci possono essere molti modi per fare le cose in perl, ma dove esiste una libreria molto standard per fare qualcosa, dovrebbe essere utilizzata a meno che non abbia problemi propri.

<*> \n"; } }

Non ci sono sei modi per farlo, c'è il vecchio modo e il nuovo modo. Il vecchio modo è con File :: Find, e ne hai già un paio di esempi. File :: Find ha un'interfaccia di callback piuttosto terribile, è stato bello 20 anni fa, ma da allora siamo passati.

Ecco un programma di vita reale (leggermente modificato) che utilizzo per cancellare l'innesto su uno dei miei server di produzione. Usa File :: Trova :: Regola, piuttosto che File :: Trova. File :: Find :: Rule ha una bella interfaccia dichiarativa che legge facilmente.

Randal Schwartz ha anche scritto File :: Finder, come wrapper su File :: Find. È abbastanza carino ma non è davvero decollato.

#! /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 :: Trova è il modo giusto per risolvere questo problema. Non è utile reimplementare elementi già esistenti in altri moduli, ma reimplementare qualcosa che si trova in un modulo standard dovrebbe davvero essere scoraggiato.

Altri hanno menzionato File :: Find, che è il modo in cui vorrei andare, ma hai chiesto un iteratore, che File :: Find non è (né File :: Find :: Rule). Potresti voler guardare File :: Next o File :: Find :: Object , che hanno interfacce iterative. Mark Jason Dominus ripercorre la propria nel capitolo 4.2.2 di Ordine superiore Perl .

Il mio metodo preferito è usare il modulo File :: Find in questo modo:

use File::Find;
find (\&checkFile, $directory_to_check_recursively);

sub checkFile()
{
   #examine each file in here. Filename is in 

Il mio metodo preferito è usare il modulo File :: Find in questo modo:

<*> and you are chdired into it's directory #directory is also available in $File::Find::dir }

C'è il mio File :: Finder , come già accennato, ma c'è anche la mia soluzione di iteratore come hash legato da Ricerca di file in modo incrementale (rivista Linux) .

Ho scritto File :: Trova :: Chiusure come set di chiusure che è possibile utilizzare con File :: Trova in modo da non dover scrivere le proprie. Ci sono un paio di funzioni mtime che dovrebbero gestire

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

Non hai davvero bisogno di usare il modulo perché l'ho progettato principalmente come un modo per guardare il codice e rubare le parti che volevi. In questo caso è un po 'più complicato perché tutte le subroutine che si occupano di stat dipendono da una seconda subroutine. Avrai presto l'idea dal codice.

Buona fortuna

L'uso dei moduli standard è davvero una buona idea, ma per disinteresse ecco le mie spalle all'approccio di base che non utilizza moduli esterni. So che la sintassi del codice qui potrebbe non essere la tazza di tè di tutti.

Potrebbe essere migliorato utilizzare meno memoria fornendo un accesso iteratore (l'elenco di input potrebbe essere temporaneamente sospeso una volta raggiunta una certa dimensione) e il controllo condizionale può essere espanso tramite il callback 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{

L'uso dei moduli standard è davvero una buona idea, ma per disinteresse ecco le mie spalle all'approccio di base che non utilizza moduli esterni. So che la sintassi del codice qui potrebbe non essere la tazza di tè di tutti.

Potrebbe essere migliorato utilizzare meno memoria fornendo un accesso iteratore (l'elenco di input potrebbe essere temporaneamente sospeso una volta raggiunta una certa dimensione) e il controllo condizionale può essere espanso tramite il callback ref.

<*>} @_ ? ( 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");

Scrivo una subroutine che legge una directory con readdir , butta fuori il ". " e " .. " directory, recluta se trova una nuova directory ed esamina i file per quello che sto cercando (nel tuo caso, ti consigliamo di utilizzare utime o stat ) . Al termine della ricorsione, ogni file avrebbe dovuto essere esaminato.

Penso che tutte le funzioni necessarie per questo script siano descritte brevemente qui: http://www.cs.cf.ac.uk/Dave/ PERL / node70.html

La semantica di input e output è un esercizio abbastanza banale che ti lascerò.

Sto rischiando di ottenere il downgrade, ma il comando IMHO 'ls' (con parametri appropriati) lo fa nel modo più noto. In questo caso potrebbe essere una buona soluzione per reindirizzare "ls" dal codice perl attraverso la shell, restituendo i risultati a un array o a un hash.

Modifica: potrebbe anche essere usato 'trova', come proposto nei commenti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top