IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Perl Discussion :

Filtrer un tableau de données


Sujet :

Langage Perl

  1. #1
    Yux
    Yux est déconnecté
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    105
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2004
    Messages : 105
    Points : 74
    Points
    74
    Par défaut Filtrer un tableau de données
    Salut,

    Désolé de poster un énième sujet du genre, mais je ne suis vraiment pas à l'aise avec les expressions régulières. J'écris un script qui permet de vérifier qu'un ou plusieurs processus s'exécutent sur une machine unix distante. Voici le début du code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #!/usr/bin/perl -w
     
    use strict;
    use Getopt::Std;
     
    my $usage = "Usage: ./ssh_test.pl -H host -p processus_list\n";
     
    my %options;
    getopt("Hp",\%options);
     
    my $H = $options{H};
    my $p = $options{p};
     
    unless ($H && $p)
    {
            print $usage;
            exit 1;
    }
     
    my @plist = split(/\|/,$p);
    my @data = `ssh $H 'ps -e'`;
    Pour vous donner une idée, si j'ajoute ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    foreach(@data)
    {
            print "$_";
    }
    J'obtiens ceci :

    PID TTY TIME CMD
    1 ? 00:00:00 init
    2 ? 00:00:00 ksoftirqd/0
    3 ? 00:00:00 events/0
    4 ? 00:00:00 khelper
    9 ? 00:00:00 kthread
    18 ? 00:00:00 kacpid
    118 ? 00:00:00 kblockd/0
    126 ? 00:00:00 khubd
    175 ? 00:00:00 pdflush
    176 ? 00:00:02 pdflush
    177 ? 00:00:00 kswapd0
    178 ? 00:00:00 aio/0
    256 ? 00:00:00 kseriod
    316 ? 00:00:01 reiserfs/0
    376 ? 00:00:00 udevd
    978 ? 00:00:00 syslogd
    981 ? 00:00:00 klogd
    1685 ? 00:00:00 rpc.portmap
    1691 ? 00:00:00 inetd
    1696 ? 00:00:00 sshd
    1709 ? 00:00:00 nfsd
    1710 ? 00:00:00 nfsd
    1711 ? 00:00:08 nfsd
    1712 ? 00:00:00 nfsd
    1713 ? 00:00:00 nfsd
    1714 ? 00:00:11 nfsd
    1715 ? 00:00:06 nfsd
    1716 ? 00:00:00 nfsd
    1718 ? 00:00:00 lockd
    1719 ? 00:00:00 rpciod/0
    1720 ? 00:00:01 rpc.mountd
    1723 ? 00:00:00 rpc.statd
    1737 ? 00:00:00 cupsd
    1750 ? 00:00:00 crond
    ...
    Ici, je n'affiche évidemment que les données brutes. J'aimerais conserver les champs 1 (PID) et 4 (nom du processus) pour chacun des processus passés en argument au script. J'ai vu qu'on pouvait utiliser la fonction map de cette façon :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    map ( {fonction($_) } @data );
    Je me proposais donc d'écrire une fonction de traitement des données mais je bloque sur le passage des arguments, dans la mesure ou cette fonction devrait matcher l'itération courante de @data (variable $_) avec une des itérations de @plist. Je ne vois pas comment passer @plist en argument, en fait. En même temps, je me dis que Perl permets sans doute avec une expression régulière bien placée d'effectuer ce traitement en une ligne, ce qui me permettrait d'utiliser la fonction map de façon plus classique (peut-être un map combiné avec un grep ?). Si quelqu'un pouvait me donner une piste, ce serait sympa...

  2. #2
    Membre du Club
    Inscrit en
    Octobre 2005
    Messages
    47
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 47
    Points : 56
    Points
    56
    Par défaut
    Pas sûr de bien comprendre le resultat que tu cherches. Pour récupérer que le 1er et le 4e champs de chaque ligne, c'est facile :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    my @entetes = shift @data; # recupérer la première ligne à part
    foreach my $ligne ( @data ) {
        my @brut = split($ligne); # l'espace est le séparateur par défaut, 
                                                 # pas besoin de le préciser
        print "Champ1 est : $brut[0] \t champ4 est : $brut[3]\n";
    }
    Qqchose comme ça ?

  3. #3
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Tu pourrais faire un truc comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    shift @data;
    my %data = map { (split)[3,0]  } @data;
    print "Ok, tous processus lancés !" if @plist == grep { exists $data{$_} } @plist;
    Bonne continuation.

    --
    Jedaï

  4. #4
    Yux
    Yux est déconnecté
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    105
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2004
    Messages : 105
    Points : 74
    Points
    74
    Par défaut
    Merci pour vos réponses Jedaï et Chostrama

    Jedaï, je ne comprends pas la première ligne de ton code :

    Où se retrouvent les données shiftées si tu ne les affectes pas à une variable ? Et pourquoi shifter dans ce cas ? Et ensuite, est-ce que tu crée un hachage %data par goût ou par nécessité ? Sinon, je ne connaissais pas la syntaxe pour préciser les champs à splitter, ça va clairement me servir. Pour tout te dire, je suis certain que ton code fonctionne mais je ne le comprends pas très bien. En fait, cette ligne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if @plist == grep { exists $data{$_} } @plist;
    ...te permets de tester la présence de l'un des éléments de @plist dans l'une des valeurs du hachage, c'est bien ça ? Mais est-ce-que tu testes la clé ou sa valeur ? Voilà, ça fait beaucoup de questions...

  5. #5
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par Yux
    Merci pour vos réponses Jedaï et Chostrama

    Jedaï, je ne comprends pas la première ligne de ton code :

    Où se retrouvent les données shiftées si tu ne les affectes pas à une variable ? Et pourquoi shifter dans ce cas ?
    Dans ton tableau @data, la première ligne (les entêtes) est inutile et génante pour les traitements suivant, je me contente donc de la supprimer par un petit shift() avant de me mettre au boulot.

    Citation Envoyé par Yux
    Et ensuite, est-ce que tu crée un hachage %data par goût ou par nécessité ?
    Je rappelle le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    my %data = map { (split)[3,0]  } @data;
    On pourrait en fait traduire ça par une boucle :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    my %data;
    foreach my $line (@data){
      my @fields = split ' ', $line;
      $data{$fields[3]} = $fields[0]; # $data{processus} = PID
    }
    En fait je crée ce hachage pour donner un accès bien plus facile et naturel aux données qui nous intéressent : les processus en train de tourner, et leur PID (puisque tu voulais le garder, je suppose que tu l'utilises). En utilisant un hachage avec comme clé le nom des processus plutôt qu'un tableau de couples (processus, pid), j'offre le moyen de vérifier gratuitement (ou presque) qu'un processus dont on connait le nom est en train de tourner, il suffit de vérifier qu'il existe un élément avec cette clé dans le hachage !

    Citation Envoyé par Yux
    Sinon, je ne connaissais pas la syntaxe pour préciser les champs à splitter, ça va clairement me servir. Pour tout te dire, je suis certain que ton code fonctionne mais je ne le comprends pas très bien. En fait, cette ligne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if @plist == grep { exists $data{$_} } @plist;
    ...te permets de tester la présence de l'un des éléments de @plist dans l'une des valeurs du hachage, c'est bien ça ? Mais est-ce-que tu testes la clé ou sa valeur ? Voilà, ça fait beaucoup de questions...
    En fait ici j'utilise une petite astuce pour vérifier que tous les processus de @plist sont en train de tourner sur la machine distante, en effet, en contexte scalaire (qui est imposé ici par le "=="), les tableaux renvoient le nombre de leurs éléments. Je me contente donc de vérifier que le tableau @plist privé de ses éléments qui ne tournent pas a le même nombre d'élément que le tableau @plist initial. En effet grep( CODE, @LIST) renvoie la liste des éléments de @LIST pour lesquels CODE a renvoyé VRAI ($_ vaut l'élément courant dans CODE). J'aurais aussi pu écrire ça (c'est un peu mieux en fait) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if grep { not exists $data{$_} } @plist == 0;
    (Autrement dit : il n'y a aucun processus de @plist qui ne soit pas en train de tourner)

    --
    Jedaï

  6. #6
    Membre du Club
    Inscrit en
    Octobre 2005
    Messages
    47
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 47
    Points : 56
    Points
    56
    Par défaut
    Citation Envoyé par Yux
    Jedaï, je ne comprends pas la première ligne de ton code :
    Où se retrouvent les données shiftées si tu ne les affectes pas à une variable ? Et pourquoi shifter dans ce cas ?
    C'est pour se débarasser de la première ligne, avec les entêtes. Donc pas besoin de garder la valeur.

    Citation Envoyé par Yux
    Pour tout te dire, je suis certain que ton code fonctionne mais je ne le comprends pas très bien. En fait, cette ligne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if @plist == grep { exists $data{$_} } @plist;
    ...te permets de tester la présence de l'un des éléments de @plist dans l'une des valeurs du hachage, c'est bien ça ? Mais est-ce-que tu testes la clé ou sa valeur ? Voilà, ça fait beaucoup de questions...
    Ici, exists() va tester l'existence de la clé. C'est-à-dire meme si sa valeur est undef.

    hth

  7. #7
    Yux
    Yux est déconnecté
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    105
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2004
    Messages : 105
    Points : 74
    Points
    74
    Par défaut
    Ah oui, bien vu pour la première ligne, j'étais distrait

    J'ai juste une petite remarque : c'est vrai que le hachage est une solution élégante pour stocker les données, mais il ne permet pas de contrôler l'exécution concurrente de plusieurs instances du même processus. Il faudrait peut-être associer une liste de PIDs à la clé ?

    Sinon, merci pour vos explications, je vois de quoi il retourne. Dans un style qui m'est plus accessible, j'utiliserais quelque chose comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    shift @data;
    my %data = map { (split)[3,0] } @data;
     
    foreach(@plist)
    {
            if(exists($data{$_}))
            {
                    print "Process: $_ PID: $data{$_}\n";   # Par exemple
            }
    }
    Le point faible est qu'il faut spécifier lors de l'appel du script le nom exact du processus (j'imagine que si la casse n'est pas la même, ça ne marchera pas non plus). Qu'en pensez-vous ?

  8. #8
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Tout dépend de ce que tu veux faire exactement, tu n'as pas été très précis là-dessus...

    --
    Jedaï

  9. #9
    Yux
    Yux est déconnecté
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    105
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2004
    Messages : 105
    Points : 74
    Points
    74
    Par défaut
    En fait, il s'agit d'un plugin de supervision. Il reçoit donc en arguments (en plus de l'IP de la machine cible) une liste de processus séparés par un |. L'idée est ensuite d'afficher les données dans une page web, je dois donc encore écrire le code qui les formatera de façon appropriée.

    Disons que ce n'est pas indispensable de prendre en compte toutes les instances d'un processus, mais je voudrais que le plugin soit le moins "limitatif" possible.

    Pour en revenir au hachage de références sur listes, ça paraît délicat dans la mesure où je ne pourrais pas déclarer les références a prirori (ne connaissant pas le nombre de processus passés en arguments). Une idée sur la façon de faire cela ?

    Et au fait, merci encore pour vos réponses et surtour votre patience, j'apprécie vraiment

  10. #10
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par Yux
    Pour en revenir au hachage de références sur listes, ça paraît délicat dans la mesure où je ne pourrais pas déclarer les références a prirori (ne connaissant pas le nombre de processus passés en arguments). Une idée sur la façon de faire cela ?
    Il faut abandonner map() et faire ça de façon plus classique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    my %data;
    foreach my $line (@data){
      my @fields = split ' ', $line;
      push @{$data{$fields[3]}},  $fields[0]; # $data{processus} = [ PIDs ]
    }
    Par contre si tu veux une mise en correspondance plus souple des deux listes de processus, il faut envisager des mécanismes assez différents, en as-tu vraiment besoin ?

    --
    Jedaï

  11. #11
    Yux
    Yux est déconnecté
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    105
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2004
    Messages : 105
    Points : 74
    Points
    74
    Par défaut
    Salut,

    Merci de votre aide et désolé pour ma réponse tardive, j'ai repris ça à l'instant. Au final, j'ai fait quelque chose comme ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    my @plist = split(/:/,$p);
    @plist = sort @plist;
     
    my @output = `ssh $H 'ps -e'`;
    shift @output;
     
    @output = map { (split)[3,0] } @output;
     
    my @data;
    my %data;
     
    while(@output)
    {
            my $process = shift @output;
            my $PID = shift @output;
            foreach(@plist)
            {
                    if ($_ eq $process)
                    {
                            push(@data,$process,$PID);
                    }
            }
    }
     
    %data = @data;
     
    %data = @data;
     
    my $exit_status = $OK;
    my $space = '<TD> </TD>';
     
    my $exit_string  = "<TABLE>";
       $exit_string .= "<TR><TD ALIGN=LEFT>Processus</TD>";
       $exit_string .= "$space";
       $exit_string .= "<TD ALIGN=LEFT>PID</TD>";
       $exit_string .= "$space";
       $exit_string .= "<TD ALIGN=LEFT>Statut</TD></TR>";
     
    foreach(@plist)
    {
            if (not exists $data{$_})
            {
                    $exit_string .= "<TR><TD><font color=red>$_</font></TD>";
                    $exit_string .= "$space";
                    $exit_string .= "<TD><font color=red>-</font></TD>";
                    $exit_string .= "$space";
                    $exit_string .= "<TD><font color=red>CRITICAL</font></TD></TR>";
                    if ($exit_status != $CRIT)
                    {
                            $exit_status = $CRIT;
                    }
            }
    }
     
    while(@data)
    {
            my $process = shift @data;
            my $PID = shift @data;
            $exit_string .= "<TR><TD>$process</TD>";
            $exit_string .= "$space";
            $exit_string .= "<TD>$PID</TD>";
            $exit_string .= "$space";
            $exit_string .= "<TD>OK</TD></TR>";
    }
     
    $exit_string .= "</TABLE>";
    print $exit_string;
    exit $exit_status;
    Comme vous pouvez le constater, le but était de générer un tableau HTML. Pour ce qui est de la méthode, je suis d'accord avec Jedaï sur le fait qu'il est bien plus élégant (et commode ) de travailler avec des hachages, mais le fait d'utiliser des listes est le seul moyen que j'ai trouvé pour superviser plusieurs instances du même processus. Après, je passe peut-être à côté de quelque chose, je ne suis pas très expérimenté en perl.

    Si vous avez des suggestions pour améliorer/épurer le code, je suis preneur...

  12. #12
    Membre du Club
    Inscrit en
    Octobre 2005
    Messages
    47
    Détails du profil
    Informations forums :
    Inscription : Octobre 2005
    Messages : 47
    Points : 56
    Points
    56
    Par défaut
    Puisque tu demandais des suggestions...

    Pour les chaînes longues, sur plusieurs lignes, plutôt que d'écrire à chaque ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    $exit_string .= "<TABLE>";
    #etc.
    tu peux utiliser, entre autres, cet opérateur de citation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    my $html = qq{
    <table>
    <le contenu de la table />
    $donnees
    </table>};
    C'est ce que je fais, il y a d'autres possibilités.

    Si j'ai bien compris, tu as conservé la liste des processus, plutôt que le hash, au cas où l'utilisateur demanderait plusieurs fois le meme nom de processus. Le hash sont tellement pratiques, que je pense que j'aurais fait qqch comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
     
     
    my %proc;
    foreach my $processus ( split ':', $p) {
         my $key = lc( $processus ); #éviter les pb de casse
        ++$proc{$key}; 
    }
     
    ## ensuite, une "array of arrays" pour les données
    # ...recuperer @output
    @output = map { [ (split)[3,0] ] } @output;
     
    # supprimer les éléments qui ne nous intéressent pas
    @output = grep { exists $proc{ $_->[0] } } @output;
     
    my ( @bon, @mauvais );
     
    foreach my $pref ( @output ) {
        my $processus = lc( $pref->[0] );
        if ( $proc{$processus } ) {
    	push( @bon, $pref );
     
        }
        else {
    	push( @mauvais, $pref );
        }
        # baisser le décompte des processus
        if ( $proc{$processus} == 1 ) {
    	$proc{$processus} = undef;
        } else {
    	--$proc{$processus};
        }
    }
     
    foreach my $pref ( @bon ) {
        # print qqch avec $pref->[0] et $pref->[1]
    }
     
    foreach my $pref ( @mauvais ) {
        # print qqch avec $pref->[0] et $pref->[1]
    }
    Ce n'est pas testé, bien sûr.

  13. #13
    Yux
    Yux est déconnecté
    Membre régulier
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    105
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Janvier 2004
    Messages : 105
    Points : 74
    Points
    74
    Par défaut
    Ok chostrama, merci pour tes conseils. Ta méthode me plaît bien effectivement. Je crois que je peux me permettre de passer ce topic en résolu.

    See you

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 05/03/2013, 10h09
  2. Réponses: 7
    Dernier message: 15/12/2005, 14h24
  3. exporter un tableau de donnée vers un document word
    Par demerzel0 dans le forum Access
    Réponses: 2
    Dernier message: 04/11/2005, 11h57
  4. [Collections] Transformer un tableau de données en une chaîne
    Par NATHW dans le forum Collection et Stream
    Réponses: 12
    Dernier message: 03/06/2004, 16h44
  5. [CR] Filtrer pour une période donnée
    Par liberio dans le forum SAP Crystal Reports
    Réponses: 6
    Dernier message: 21/04/2004, 16h32

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo