[Perl] Besoin d'aide

Bonjour a tous,

je débute dans la programmation, je suis nul mais j'essaye d'apprendre...
Je suis actuellement sur un script perl, l'objectif est de lire un fichier qui se presentent sous cette forme:

xxxxxxxxx:yyyyyyyyyy
lllllllllllllll:ppppppppp
zzzz:ooooooo

Je veut lire ce fichier (ca c'est ok) et recupérer dans un tableau1 xxxxxxxxx
llllllllllll
zzzzzzz

et récupérer dansun tableau 2: yyyyyyyyyyyyy
ppppppppp
oooooooooo

voici le debut de mon script, je suis bloqué a cette étape de lecture pourriez vous m'aidez svp.

!/usr/bin/perl


open(FIC,"/etc/apache2/vhost.conf") or die ("Erreur lors de l'ouverture du fichier.conf");

@fic = <FIC> ;
foreach $line (@fic)
{
   print $line;
}
close("FIC");


Cdt,

Merci d'avance.
Configuration: Linux
Mozilla 1.9.0.14

41 réponses

Résumé de la discussion

Un débutant en programmation cherche à lire un fichier au format xxxxxxxxx:yyyyyyyyyy lllllllllllllll:ppppppppp zzzz:ooooooo et à constituer deux tableaux, l'un avec les premières valeurs et l'autre avec les secondes. La solution retenue passe par lire deux fichiers: access.log et vhost.conf, extraire les paires clé/valeur par délimiteur ':'; puis trier les lignes de access.log en fonction des clés présentes dans le premier tableau. On construit ensuite une structure de hachage pour regrouper les lignes associées à chaque clé et on affiche les résultats triés et les éléments non correspondants. Des échanges ultérieurs évoquent des tests dans divers environnements et soulignent l'importance d'adapter les droits et l'emplacement des fichiers (ex. tests en root, répertoires spécifiques) pour obtenir des résultats reproductibles.

Bobot (l’IA à votre service)
  1. oups besoin de ton aide :(

    je m'apprete a faire qq test avant de presenter ça...

    mais ca ne merche pas, voici je que j'ai quand j'execute le script:

    Use of uninitialized value $1 in hash element at script_V2.0.pl line 13, <FF> line 73.
    Use of uninitialized value $1 in hash element at script_V2.0.pl line 13, <FF> line 74.
    Use of uninitialized value $1 in hash element at script_V2.0.pl line 13, <FF> line 75.
    Use of uninitialized value $1 in concatenation (.) or string at script_V2.0.pl line 21, <FF> line 75.
    Use of uninitialized value within %vhost in concatenation (.) or string at script_V2.0.pl line 23, <FF> line 75.
    E/S: Aucun fichier ou dossier de ce type
    
    
    0
    1. Modérateur
      Re,

      #!/usr/bin/perl
      open F,"access.log" or die "E/S: $!\n";
      open FF,"vhost.conf" or die "E/S: $!\n";
      my (%h,%vhost);
      # la date du système
      chomp(my $date=qx/date '+%d%m%Y'/);
      
      # la cle du hash    = le nom de domaine
      # la valeur du hash = le répertoire /mnt/....
      while(<FF>){
        s/\s*$//g;
        /^(.*):(.*)/;
        $vhost{$1}=$2;
      }
      
      # ouverture d'un handle de fichier en écriture
      # pour chaque fichier (dans /mnt/...)
      for(keys %vhost){
        s/^\s*//g;
        /^(.*?)\..*/;
        my $vhostjjmmaa = "$1.$date";
        my $handle=$_;
        open $handle,">$vhost{$_}${vhostjjmmaa}.log" or die "E/S: $!\n";
      }
      
      # access.log est parcouru une seule fois
      while(<F>){
      # saute les lignes qui ne correspondent aux nom domaines
        next unless /^((.*?)\..*?)\s.*/;
        my $FH=$1; # je stocke la chaîne de caractères
                   # qui corresponde aux handle ouvert
        print $FH $_; # j'écris dans le handle (le fichier /mnt/../vhostjjmmaa.log
      }
      
      # fermeture des handles ouverts
      for(keys %vhost){ close $_;}
      
      # suppression des fichiers vides dans /mnt/..
      for(values %vhost){ # pour chaque vhost
        opendir(REP,$_);  # handle vers /mnt/..
        while(my $fic=readdir REP){ # chaque fichier
          next if $fic=~/\.\.?$/;   # sauf . et ..
          chomp(my $taille = qx/du -sh "$_$fic"/); # je calcule la taille
          unlink "$_$fic" if $taille=~/^0/;        # si zéro je supprime
        }
        close REP; # fermeture de handle en cours
      }
      
      __END__
      
      0
      1. merci beaucoup je pourrais tester que en live car focément j'ai pas les droit pour tester ça seul...

        Peut tu me commenter tout ça ?

        Merci encore pour cette contribution énorme !
        0
        1. Modérateur
          Re,

          J'ai créer les répertoire selon vhost.conf.
          Les fichiers logs sont créer dans le répertorie correspondant.
          Les fichiers vides seront supprimé à la fin.
          Le test sur le même fichier de 3.4 Go varie chez moi (en fonction de la charge de mon pc, puisque je fais d'autre choses aussi ;-) entre 8minutes et 10 minutes
          A savoir que peut être les noms des fichiers on le nom
          nomdomainejusque1erpoint.jjmmaa.log

          Voici le script.
          Tu me diras le résultat des tests.
          A savoir que le script doit être exécuter en root vu qu'on écrit un peu partout dans /mnt
          
          #!/usr/bin/perl
          open F,"access.log" or die "E/S: $!\n";
          open FF,"vhost.conf" or die "E/S: $!\n";
          my (%h,%vhost);
          chomp(my $date=qx/date '+%d%m%Y'/);
          
          while(<FF>){
            s/\s*$//g;
            /^(.*):(.*)/;
            $vhost{$1}=$2;
          }
          
          for(keys %vhost){
            s/^\s*//g;
            /^(.*?)\..*/;
            my $vhostjjmmaa = "$1.$date";
            my $handle=$_;
            open $handle,">$vhost{$_}${vhostjjmmaa}.log" or die "E/S: $!\n";
          }
          
          while(<F>){
            next unless /^((.*?)\..*?)\s.*/;
            my $FH=$1;
            print $FH $_;
          }
          
          for(keys %vhost){ close $_;}
          
          for(values %vhost){
            opendir(REP,$_);
            while(my $fic=readdir REP){
              next if $fic=~/\.\.?$/;
              chomp(my $taille = qx/du -sh "$_$fic"/);
              unlink "$_$fic" if $taille=~/^0/;
            }
            close REP;
          }
          
          __END__
          
          0
          1. Pfff quel boulet !

            Dslé j'ai pas cliquer sur la fenêtre qui donne le totalité du post (fatigue lol)

            C'est un tres bonne nouvelle tout ca ! franchement Bravo !

            en ce qui concerne ta question:

            Mais après cette histoire, je me pose une question.
            Pourquoi ne pas configurer une des directives (TransferLog, CustomLog) directement dans la configuration des VirtualHost ?
            Comme ça il n'y a plus besoin d'un script Perl pour avoir les logs par vhost.
            Apache s'occupera lui même.
            


            Oui je suis d'accords avce toi, je ne connais pas cette fonction je vais faire qq recherches la dessus.
            Mais vu y'aura des fichiers genére genre dl.082009.log pour un vhost et peut etre un autre du même nom pour un autre vhost (vu que dl représente juste le serivce de download)... tu voit ce que je veux dire ???
            0
            1. Modérateur
              Re,

              Ben, le script est dans le message.
              Il est complétement modifié.
              Je parcours le fichier access.log une seule fois, d''où le gagne de performances.

              Mon raisonnement est dans le message 33
              0
              1. Modérateur
                Salut,

                Pour la version du message 13
                Correction : Pour la version du message 31
                0
                1. C'est un test en version non optimisé ? 410 mega en used, en effet pour un fichier de test access.log (de 200 méga, si mes souvenirs sont bons) ça fais beaucoup...

                  Je vois que tu me concoctes un truc au ptits oignons, je ne sais pas comment te remercier !

                  Tu aimes le vin de bourgogne ?
                  0
              2. Modérateur
                Salut,

                Je viens de faire un test avec la dernière version que je t'ai donné et aussi avec une nouvelle que j'ai écrit selon le raisonnement de mon dernier message (Message N° 33 )

                Le test je l'ai fait sur
                lami20j@debian:~$ cat /proc/cpuinfo
                processor : 0
                vendor_id : AuthenticAMD
                cpu family : 6
                model : 8
                model name : AMD Athlon(tm) XP 2200+
                stepping : 0
                cpu MHz : 1793.189

                lami20j@debian:~$ free
                total used free shared buffers cached
                Mem: 516300 411508 104792 0 10540 170024
                -/+ buffers/cache: 230944 285356
                Swap: 393552 112172 281380

                lami20j@debian:~$ uname -a
                Linux debian 2.6.26-2-686 #1 SMP Wed Aug 19 06:06:52 UTC 2009 i686 GNU/Linux


                J'ai utilisé un fichier access.log de 3.4 Go et plus de 20 millions lignes
                lami20j@debian:/mnt/hda5/accesslog$ wc -l access.log; du -h access.log
                20054931 access.log
                3,4G    access.log


                Pour la version du message 13 après presque 2heures j'ai eu Out of memory sans avoir le résultat voulu
                lami20j@debian:/mnt/hda5/accesslog$ time perl stock_accesslog_v4.pl
                Complété
                
                real    116m2.399s
                user    2m49.391s
                sys     0m42.367s
                

                En revanche avec une autre version le traitement a été effectué correctement dans 8minutes 32secondes
                lami20j@debian:/mnt/hda5/accesslog$ time perl stock_accesslog_v5.pl
                
                real    8m39.852s
                user    2m51.499s
                sys     0m31.422s
                

                Il faudra qu'on parle en ce qui concerne cette dernière version.(Le chemin des logs n'est pas pris en compte. Un fichier vide sera crée pour chaque vhost, ce qui normalement ne dérange pas vu le gagne de performance.)
                #!/usr/bin/perl
                open F,"access.log" or die "E/S: $!\n";
                open FF,"vhost.conf" or die "E/S: $!\n";
                my (%h,%vhost);
                chomp(my $date=qx/date '+%d%m%Y'/);
                
                while(<FF>){
                  /^(.*):.*/;
                  $vhost{$1}++;
                }
                
                for(keys %vhost){
                  /^(.*?)\..*/;
                  my $vhostjjmmaa = "$1.$date";
                  open $vhostjjmmaa,">>${vhostjjmmaa}.log" or die "E/S: $!\n";
                }
                
                while(<F>){
                  next unless /^((.*?)\..*?)\s.*/;
                  my $FH = "$2." . $date;
                  print $FH $_;
                }
                
                for(keys %vhost){
                  /^(.*?)\..*/;
                  close "$1.$date";
                }
                __END__
                

                Mais après cette histoire, je me pose une question.
                Pourquoi ne pas configurer une des directives (TransferLog, CustomLog) directement dans la configuration des VirtualHost ?
                Comme ça il n'y a plus besoin d'un script Perl pour avoir les logs par vhost.
                Apache s'occupera lui même.

                0
                1. Modérateur
                  Salut,

                  Le dernier script que je te donné, selon moi, fonctionne (j'ai testé sur les deux fichiers que tu m'as donné).
                  En revanche access.log n'est pas grand.
                  J'ai fait un test sur un access.log de 200 Mo et le traitement a été fait en 1 minute (Athlon XP 1,5 Mhz, 512 DDRAM + 400 Mo swap)

                  Toutefois, je pense qu'on peut encore améliorer.
                  Si tu regardes bien, dans le script le fichiers access.log est parcouru plusieurs fois.
                  - une fois quand on stocke les host (voir la 1ère boucle while)
                  - dans la boucle for est parcouru en fonctionne de nombre de host trouvé et qui correspondent avec vhost
                  Donc ce n'est pas très efficace sur un fichier de quelque Go par exemple.

                  L'idéal sera de parcourrir le fichier access.log une seule fois et d'écrire dans vhostjjmmaa.log les lignes trouvées.

                  Je suis en train de penser à une solution.
                  D'ailleurs ce qui t'interesse c'est qu'en partant de l'access.log tu dois créer un vhostjjmmaa.log pour chaque vhost qui se trouve dans vhost.

                  Donc j'ai suivi ton raisonnement, et je suis parti à la recherche d'abord de vhost dans access.log (ce qui implique le 1ère parcours de access.log) et ensuite reprendre le vhost.conf pour voir si les vhost correspondent et ensuite pourcourrir le access.log pour chaque vhost.

                  Mon idée est en fait de lire le vhost.conf et ensuite de parcourrir une seule fois access.log et dès qu'en vhost est rencontré alors on écrit dans le fichier vhostjjmmaa.log.
                  Tu vois ce que je veux dire?

                  0
                  1. Sinon tu donnes des cours ? Combien de l'heure ? J'achète !

                    Merci ca fais vraiment plaisir de voir des gens comme toi, je test ça demain.
                    0
                    1. Modérateur
                      Re,

                      Essaie plutôt ça et dit si ça te conviens.
                      #!/usr/bin/perl
                      use strict;use warnings;
                      open F,"access.log"  or die "E/S: $!\n";
                      my %h;
                      
                      while(<F>){
                        next unless /^(.*?)\s.*/;
                        $h{$1}++;
                      }
                      for (keys %h){
                        my $z=$_;
                        /^(.*?\.).*/;
                        chomp(my $fic="$1" . qx/date '+%d%m%Y'/);
                        my $c = grep { /^$z/ } qx/cat vhost.conf/;
                        if($c>0){
                          open FFF,">>$fic" or die "E/S : $!\n";
                          print FFF grep { /^$z/ } qx/cat access.log/;
                          close $fic;
                        }
                      }
                      __END__
                      
                      0
                      1. Tu assures en attendant, je test ca demain !
                        0
                        1. Modérateur
                          Salut,

                          Essaie ce script.
                          Toutefois, il n'est pas très performant vu qu'il y a trop d'appels système.
                          En fait vu le résultat que tu veux obtenir, je ne vois pas à quoi ça sers de faire un tri par nombre d'occurrences, à moins que tu as un traitement particulier dont tu n'as pas parlé.

                          Je vais pensé l'week-end prochain pour une optimisation.

                          #!/usr/bin/perl
                          use strict;use warnings;
                          
                          open F,"access.log"  or die "E/S: $!\n";
                          open FF,"vhost.conf" or die "E/S: $!\n";
                          
                          # 1ère partie : obtenir les tableaux
                          my (@f1,@f2,@reste,%h);
                          push @f1,(split ":",$_)[0] and push @f2,(split ":",$_)[1] while(<FF>);
                          chomp@f2;
                          
                          # 2ème partie : faire le tri par rapport au tableau @f1
                          while (<F>){
                                    /^(.*?)\s/;push @{$h{$1}},$_ if grep{/$1/} @f1;
                                      push @reste,$_ unless grep{/$1/}@f1;
                          }
                          
                          my @trie = map  { $_->[1] }
                                     sort { $b->[0] <=> $a->[0] }
                                     map  { [ @$_+0, $_ ] } values %h;
                          
                          #print sort @$_ for @trie;print for sort @reste;
                          
                          for my $e(@trie){
                            for (@{$e}){
                              /^((.*?\.).*?)\s.*/;
                              my $fic = "$2" . qx/date '+%d%m%Y'/;
                              open FFF,">>$fic" or die "E/S : $!\n";
                              print FFF grep { /^$1/ } qx/cat vhost.conf/;
                              close $fic;
                          
                            }
                          }
                          __END__
                          
                          0
                          1. Modérateur
                            Re,

                            Essaie ça
                            #!/usr/bin/perl
                            use strict;use warnings;
                            
                            open F,"access.log"  or die "E/S: $!\n";
                            open FF,"vhost.conf" or die "E/S: $!\n";
                            open FFF,">vhostjjmmaaaa.log" or die "E/S: $!\n";
                            
                            # 1ère partie : obtenir les tableaux
                            my (@f1,@f2,@reste,%h);
                            push @f1,(split ":",$_)[0] and push @f2,(split ":",$_)[1] while(<FF>);
                            chomp@f2;
                            
                            # 2ème partie : faire le tri par rapport au tableau @f1
                            while (<F>){
                                      /^(.*?)\s/;push @{$h{$1}},$_ if grep{/$1/} @f1;
                                        push @reste,$_ unless grep{/$1/}@f1;
                            }
                            
                            my @trie = map  { $_->[1] }
                                       sort { $b->[0] <=> $a->[0] }
                                       map  { [ @$_+0, $_ ] } values %h;
                            
                            print sort @$_ for @trie;print for sort @reste;
                            
                            for my $e(@trie){
                              for (@{$e}){
                                /^(.*?)\s.*/ and print FFF grep { /^$1/ } qx/cat vhost.conf/;
                              }
                            }
                            __END__
                            
                            0
                            1. Désolé j'ai du mal m'exprimé.

                              Chaque lignes de logs pour ce groupe d'occurences (en s'occupant en 1er du vhost qui a le plus d'occurences) devra etre gréppé du résultat et etre placé dans un fichier vhostjjmmaaaa.log a l'adresse "pathlog" qui lui correspond dans vhost.conf (ex /mnt/filer-genet-3/a/b/ab......com/ftp/logs/).

                              J'ai été plsu claire ? :(

                              lol
                              0
                              1. Modérateur
                                Re,

                                de les placées dans un fichier (vhostjjmmaaaa.log)
                                ça ce n'est pas difficile

                                dans le path log correspondant a cette ocurrence(@f2).
                                Ça, je n'ai pas compris.

                                Tu peux me donner un exemple?
                                0
                                1. tiens après exucution:

                                  sudo perl scriptlol.pl
                                  6
                                  2565111"access.log" [readonly][converted] 65641L, 17119557C
                                  0
                                  1. Modérateur
                                    Re,

                                    Mets dans le script ce ces lignes et exécute le script.
                                    Ensuite affiche le résultat.

                                    print scalar @trie,"\n";
                                    print scalar @$_ for @trie;print for sort @reste;

                                    0
                                    1. autant pour moi ca marhce très bien !

                                      ;)

                                      L'objectif suivant c'est de faire un cut de chaque groupe d'oocurences de ce que l'on obtiens de "sort @$_ for @trie" et de les placées dans un fichier (vhostjjmmaaaa.log)dans le path log correspondant a cette ocurrence(@f2).

                                      C'est dur ? tu t'y prendrais comment ?
                                      0
                                      • 1
                                      • 2
                                      • 3