Question

Vous avez parfois entendu dire à propos de Perl qu'il pouvait y avoir 6 façons différentes d'aborder le même problème. Les bons développeurs Perl ont généralement des idées bien motivées pour choisir entre les différentes méthodes de mise en œuvre possibles.

Donc, un exemple de problème Perl:

Un script simple qui itère de manière récursive dans une structure de répertoires, recherchant les fichiers modifiés récemment (après une certaine date, qui serait variable). Enregistrez les résultats dans un fichier.

La question, pour les développeurs Perl: quel est le meilleur moyen d’y parvenir?

Était-ce utile?

La solution

Cela ressemble à un travail pour Fichier :: Trouver :: Règle :

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

Autres conseils

Lorsque le problème est résolu principalement par les bibliothèques standard, utilisez-les.

File :: Find dans ce cas fonctionne bien.

Il y a peut-être plusieurs façons de faire les choses en perl, mais lorsqu'une bibliothèque très standard existe pour faire quelque chose, elle devrait être utilisée sauf si elle a des problèmes qui lui sont propres.

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

Lorsque le problème est résolu principalement par les bibliothèques standard, utilisez-les.

File :: Find dans ce cas fonctionne bien.

Il y a peut-être plusieurs façons de faire les choses en perl, mais lorsqu'une bibliothèque très standard existe pour faire quelque chose, elle devrait être utilisée sauf si elle a des problèmes qui lui sont propres.

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

Lorsque le problème est résolu principalement par les bibliothèques standard, utilisez-les.

File :: Find dans ce cas fonctionne bien.

Il y a peut-être plusieurs façons de faire les choses en perl, mais lorsqu'une bibliothèque très standard existe pour faire quelque chose, elle devrait être utilisée sauf si elle a des problèmes qui lui sont propres.

<*> \n"; } }

Il n'y a pas six façons de faire cela, il y a l'ancienne et la nouvelle. L'ancienne méthode utilise File :: Find, et vous en avez déjà quelques exemples. File :: Find a une interface de rappel assez affreuse, c'était cool il y a 20 ans, mais nous avons évolué depuis.

Voici un programme réel (légèrement modifié) que j’utilise pour nettoyer le fichier sur l’un de mes serveurs de production. Il utilise File :: Find :: Rule, plutôt que File :: Find. File :: Find :: Rule a une belle interface déclarative qui se lit facilement.

Randal Schwartz a également écrit File :: Finder, qui encapsule File :: Find. C'est assez sympa mais ça n'a pas vraiment décollé.

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

Fichier :: Trouver est la bonne façon de résoudre ce problème. Il est inutile de réimplémenter des éléments déjà présents dans d'autres modules, mais il convient de décourager la réimplémentation de quelque chose qui se trouve dans un module standard.

D'autres ont mentionné File :: Find, ce qui est la voie à suivre, mais vous avez demandé un itérateur, mais File :: Find n'est pas (ni File :: Find :: Rule). Vous voudrez peut-être consulter Fichier :: Suivant ou File :: Find :: Object , qui ont une interface itérative. Mark Jason Dominus explique comment créer le vôtre au chapitre 4.2.2 du Higher Order Perl . / p>

Ma méthode préférée consiste à utiliser le module File :: Find comme suit:

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

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

Ma méthode préférée consiste à utiliser le module File :: Find comme suit:

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

Il existe mon Fichier :: Finder , comme déjà mentionné, mais il y a aussi ma solution d'itérateur lié à Recherche incrémentielle de fichiers (Linux Magazine) .

J'ai écrit Fichier :: Rechercher :: Fermetures comme un ensemble de fermetures que vous pouvez utiliser avec File :: Find pour que vous n'ayez pas à écrire la vôtre. Il y a quelques fonctions mtime qui devraient gérer

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

Vous n'avez pas vraiment besoin d'utiliser le module, car je l'ai principalement conçu pour vous permettre de consulter le code et de voler les parties que vous vouliez. Dans ce cas, c'est un peu plus compliqué, car tous les sous-programmes traitant de stat dépendent d'un deuxième sous-programme. Cependant, vous aurez rapidement l’idée du code.

Bonne chance,

Utiliser des modules standard est certes une bonne idée mais, par intérêt, je reviens à l’approche de base sans aucun module externe. Je sais que la syntaxe de code ici pourrait ne pas être la tasse de thé de tout le monde.

Il pourrait être amélioré en utilisant moins de mémoire en fournissant un accès itérateur (la liste d'entrées pourrait être temporairement suspendue une fois qu'elle a atteint une certaine taille) et le contrôle conditionnel peut être étendu via le code de rappel.

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{

Utiliser des modules standard est certes une bonne idée mais, par intérêt, je reviens à l’approche de base sans aucun module externe. Je sais que la syntaxe de code ici pourrait ne pas être la tasse de thé de tout le monde.

Il pourrait être amélioré en utilisant moins de mémoire en fournissant un accès itérateur (la liste d'entrées pourrait être temporairement suspendue une fois qu'elle a atteint une certaine taille) et le contrôle conditionnel peut être étendu via le code de rappel.

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

J'écris un sous-programme qui lit un répertoire avec readdir , jette le texte """. et " .. " repertes, recurses si elle trouve un nouveau repertoire, et examine les fichiers pour ce que je cherche (dans votre cas, vous voudrez utiliser utime ou stat ) . À la fin de la récurrence, chaque fichier devrait avoir été examiné.

Je pense que toutes les fonctions dont vous auriez besoin pour ce script sont décrites brièvement ici: http://www.cs.cf.ac.uk/Dave/ PERL / node70.html

La sémantique des entrées et des sorties est un exercice assez trivial que je vous laisserai.

Je risque d’avoir un vote négatif, mais la commande IMHO 'ls' (avec les paramètres appropriés) le fait de la manière la plus connue. Dans ce cas, il pourrait s’avérer une très bonne solution de diriger le 'ls' du code perl vers le shell, en renvoyant les résultats dans un tableau ou un hachage.

Modifier: Il pourrait également être utilisé, comme proposé dans les commentaires.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top