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 :

Use of uninitialized value $_ in pattern match (m//)


Sujet :

Langage Perl

  1. #1
    Membre régulier
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2013
    Messages : 195
    Points : 121
    Points
    121
    Par défaut Use of uninitialized value $_ in pattern match (m//)
    Bonjour,

    avec le bout de code ci-dessous (je n'ai pas mis la suite inutile pour le sujet), j'ai autant de message "Use of uninitialized value $_ in pattern match (m//)" qu'il y a de ligne lu dans le fichier à lire si je ne rajoute pas dans le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    no warnings 'uninitialized';

    La partie qui cause ce message est le second bloque if.
    Même si je met le second bloc if en premier, il génère les mêmes messages.

    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
    use warnings;
    use strict;
    use Data::Dumper;
    #no warnings 'uninitialized';
     
     
    ####################### DEBUT CODE #######################
     
    die "Maximum 1 arguement" if (@ARGV >= 2);
     
    open (my $fh, "<", "$ARGV[0]") or die "Cannot open $!";
     
    my %hash_acl;
    my $ip_local_acl_name;
    my $acl_name;
    my $acl_ligne;
    my @ACL_SSH;
     
    while (my $ligne = <$fh>) {
    	chomp($ligne);
    	if ($ligne =~ /^ip access-list/) {
    		$acl_name = $ligne;
    		} 
    	if ($ligne =~ /^\spermit/i || /^\sdeny/i) {
    		$acl_ligne = $ligne;
    		push @{$hash_acl{"$acl_name"}}, $acl_ligne;
    		}
    	if ($ligne =~ /^ip local access-list/) {
    		my $IN_OUT = (split (/ /, $ligne)) [-1];
    		if ( $IN_OUT =~ /in/) { # Véerefie que l'ACL est en IN
    			$ip_local_acl_name = (split (/ /, $ligne)) [-2]; # Récupere le nom de l'ACL dans IP  LOCAL
    			} else {
    			die "PAS ACL EN IN EN IP LOCAL\n";
    			}
    		}
    	}
    C'est un comportement normal?

    Merci.

  2. #2
    Membre habitué
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Mars 2015
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2015
    Messages : 138
    Points : 138
    Points
    138
    Par défaut
    Rapidement, au boulot sans tester, il me semble que la ligne suivante a un souci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ($ligne =~ /^\spermit/i || /^\sdeny/i) {
    il aurait peut-être fallu écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if ( $ligne =~ /^\spermit | ^\sdeny/ix ) {
    voire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ( $ligne =~ /^ \s (permit|deny)/ix) {
    Bon, je retourne à mon code :-/

  3. #3
    Membre régulier
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2013
    Messages : 195
    Points : 121
    Points
    121
    Par défaut
    Bonjour ,

    les deux solutions fonctionnent !
    Je vais me pencher sur le pourquoi du || pause problème.

    Merci

  4. #4
    Membre régulier
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2013
    Messages : 195
    Points : 121
    Points
    121
    Par défaut
    A priori le | est utilisé dans le context des match.

  5. #5
    Membre confirmé
    Avatar de cmcmc
    Homme Profil pro
    Inscrit en
    Juillet 2013
    Messages
    316
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juillet 2013
    Messages : 316
    Points : 641
    Points
    641
    Par défaut
    Citation Envoyé par caramon _majere Voir le message
    Je vais me pencher sur le pourquoi du || pause problème.
    indice : ajoute la ligne
    avant ton bloc while et le warning devrait disparaître...
    Sauf indication contraire tous les codes que je présente sont utilisables et testés (mais sans garantie d'aucune sorte)
    J'apporte beaucoup de soin à la rédaction de mes posts et apprécie les retours donc merci de s'il vous paraissent pertinents ou utiles
    Lazyness, Impatience and Hubris are good for you

  6. #6
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Bonjour,
    cette ligne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if ($ligne =~ /^\spermit/i || /^\sdeny/i)
    est interprétée comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if ( ($ligne =~ /^\spermit/i) || (/^\sdeny/i) )
    c'est-à-dire comme si tu avais écrit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if (  ($ligne =~ /^\spermit/i)  || ($_ =~  /^\sdeny/i)  )
    Le premier motif est comparé à $ligne, mais le second motif est comparé à $_, et ce n'est pas ce que tu veux, puisque tu veux sans doute quelque chose du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if ( ($ligne =~ /^\spermit/i) || ($ligne =~ /^\sdeny/i)  )
    C'est donc un problème de précédence des opérateurs.

    L'utilisation du module B::Deparse permet de clarifier ce genre de problèmes en montrant comment Perl interprète ton code. Par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    $ perl -MO=Deparse,-p -e '$line = "toto";  if ($ligne =~ /^\spermit/i || /^\sdeny/i) {print "OK";}'
    ($line = 'toto');
    if ((($ligne =~ /^\spermit/i) or /^\sdeny/i)) {
        print('OK');
    }
    -e syntax OK
    Regarde bien comment le module B::Deparse a ajouté des parenthèses.

    ptonnerre a donné une bonne solution.

  7. #7
    Membre régulier
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2013
    Messages : 195
    Points : 121
    Points
    121
    Par défaut
    Bonjour Lolo,

    ton explication visuel fait sauter au yeux de suite la cause et le fonctionnement.

    je testerais ce soir le module, je ne peux pas le télécharger au travail.

    Merci

  8. #8
    Membre régulier
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2013
    Messages : 195
    Points : 121
    Points
    121
    Par défaut
    J'avais retenue la solution de ponnerre suivante, elle est "intuitive"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ( $ligne =~ /^ \s (permit|deny)/ix)

  9. #9
    Membre confirmé
    Avatar de cmcmc
    Homme Profil pro
    Inscrit en
    Juillet 2013
    Messages
    316
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juillet 2013
    Messages : 316
    Points : 641
    Points
    641
    Par défaut
    Citation Envoyé par Lolo78 Voir le message
    C'est donc un problème de précédence des opérateurs.
    Absolument pas, ça n'a rien à voir avec la précédence. Peut être dans un hypothétique langage dans lequel /exprA/ || /exprB/ serait magiquement transformé en /(?:exprA|exprB)/ , et dans lequel || aurait une précédence supérieure à =~ , mais pas en Perl

    D'un point de vue développement et maintenance (qui est un sujet qui m'intéresse passablement ), on peut s'interroger sur l'origine de ce test malheureux. Personnellement, j'ai du mal à imaginer que le développeur ait initialement écrit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      if ($ligne =~ /^\spermit/i
    puis
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      if ($ligne =~ /^\spermit/i ||
    puis
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      if ($ligne =~ /^\spermit/i || /^\sdeny/i
    etc...

    Je suspecte plutôt que le code en question a commencé sa vie sous une forme du style
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    while (<$fh>) {
        chomp;
        if (/^ip access-list/) {
    	...
        } 
        if (/^\spermit/i || /^\sdeny/i) {
    	...
        }
        if (/^ip local access-list/) {
    	...
        }
    }
    peut être avec des couper/coller d'autres fragments de code, et que la variable $ligne a été ajoutée ensuite. Peut être via un remplacement sous éditeur du genre s/if (\//if ($ligne =~ \// , suivi de s/$_/$ligne/ . Cela me parait d'autant plus vraisemblable que ce sont des manips que je faisais dans le temps. Je ne les fais plus depuis que j'ai décidé de privilégier autant que possible le topic par défaut aux variables nommées.

    Tout ça pour dire que si on préfère utiliser des variables plutôt que le topic pas défaut, alors il vaut mieux le faire dès le début, plutôt que les réinjecter a postériori.

    @caramon _majere : s'il te plaît ne t'offense pas, je ne cherche absolument pas à t'assigner des pratiques contre nature ou critiquer ton code. Il m'a rappelé des étapes dans ma vie de codeur et remis en mémoire une des raisons pour laquelle je privilégie maintenant autant que possible le topic par défaut $_ aux variables nommées, raison que je trouve intéressant de documenter. Crois bien que c'est sans aucune intention blessante à ton égard.
    Sauf indication contraire tous les codes que je présente sont utilisables et testés (mais sans garantie d'aucune sorte)
    J'apporte beaucoup de soin à la rédaction de mes posts et apprécie les retours donc merci de s'il vous paraissent pertinents ou utiles
    Lazyness, Impatience and Hubris are good for you

  10. #10
    Membre confirmé
    Avatar de cmcmc
    Homme Profil pro
    Inscrit en
    Juillet 2013
    Messages
    316
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Juillet 2013
    Messages : 316
    Points : 641
    Points
    641
    Par défaut
    Citation Envoyé par caramon _majere Voir le message
    J'avais retenue la solution de ponnerre suivante, elle est "intuitive"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ( $ligne =~ /^ \s (permit|deny)/ix)
    à ceci près qu'on n'a pas besoin d'utiliser un groupe capturant. Du coup la forme suivante serait en fait marginalement mieux adaptée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ( $ligne =~ /^ \s (?:permit|deny)/ix)
    Pour ma part je trouve en fait la forme originale plutôt plus lisible si on utilise le topic par défaut $_ plutôt que la variable $ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    while (<$fh>) {
        chomp;
        if (/^ip access-list/) {
    	...
        } 
        if (/^\spermit/i || /^\sdeny/i) {
    	...
        }
        if (/^ip local access-list/) {
    	...
        }
    }
    mais c'est personnel

    Noter qu'il est parfois intéressant de faire des matches successifs séparés par || ou &&, pour utiliser les effets de court-circuit de ces opérateurs, en particulier pour des expressions régulières complexes (par exemple contenant des variables), car cela permet d'éliminer la construction de la seconde regexp si la première réussit (respectivement échoue). Par exemple dans
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    while (...) { 
      if (m/^\spermit/ || m/^\sfrobnicate $foo/) { ... }
    }
    la seconde regexp ne sera construite que pour les lignes qui ne satisfont pas la première, contrairement à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    while (...) { 
      if (m/^\spermit|^\sfrobnicate $foo/) { ... }
    }
    où (du fait de la présence de $foo) la regexp est recompilée à chaque itération de la boucle. Quand on veut optimiser le temps d'exécution, et si cette boucle est 'hot', et selon la répartition des entrées, ça peut être significatif.
    Sauf indication contraire tous les codes que je présente sont utilisables et testés (mais sans garantie d'aucune sorte)
    J'apporte beaucoup de soin à la rédaction de mes posts et apprécie les retours donc merci de s'il vous paraissent pertinents ou utiles
    Lazyness, Impatience and Hubris are good for you

  11. #11
    Membre régulier
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2013
    Messages : 195
    Points : 121
    Points
    121
    Par défaut
    Bonjour cmcmc,

    effectivement j'ai refais le test en utilisant la variable par défaut et ma construction qui ne fonctionnait pas avec une variable nommé, et la ca fonctionne.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    while (<$fh>) {
        chomp;
        if (/^ip access-list/) {
    	...
        } 
        if (/^\spermit/i || /^\sdeny/i) {
    	...
        }
        if (/^ip local access-list/) {
    	...
        }
    J'utilise indifféremment les valeurs par défaut et des fois des nommés pour les retrouver dans le script car à part gagner quelque secondes à taper sur le clavier, je ne voyais pas de différences notable.

  12. #12
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par cmcmc Voir le message
    Absolument pas, ça n'a rien à voir avec la précédence.
    C'est vrai, le terme précédence n'est pas vraiment correct ici du point de vue de la syntaxe Perl, mais, dans un certain sens, c'est un problème de précédence du point de vue de ce voulait faire le développeur et de ce qui se passe dans sa tête. Et ça n'a rien d'extraordinaire de sa part.

    Personnellement, j'ai du mal à imaginer que le développeur ait initialement écrit (...)
    Je l'imagine sans peine.

    Quand j'ai commencé à développer il y a bien longtemps, j'aurais voulu pouvoir écrire quelque chose du genre (je ne faisais pas de Perl à l'époque, mais j'utilise la syntaxe Perl dans cet exemple pour rester dans le sujet de cette rubrique du forum):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    # code hypothétique non correct
    if ($c == 0 || 1) { # ...
    et j'ai appris à mes dépens que ça ne marchait pas et qu'il fallait écrire un truc du genre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if ($c == 0 || $c == 1) { # ...
    ce qui n'est pas très naturel. Dans un langage naturel comme le français, on dirait "si $c égale 0 ou 1", et non pas "si $c égale 0 ou $c égal 1".

    J'en ai rêvé à l'époque, et Perl 6 l'a fait avec le mécanisme des jonctions:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    # Exemple Perl 6
    > my $c = 1;
    1
    > say "OK" if $c == 0 | 1;
    OK
    >
    0 | 1 est ici une jonction (ou "superposition quantique") qui peut prendre soit la valeur 0, soit la valeur 1, ou plutôt qui possède les deux valeurs simultanément (de même que le chat de Schrödinger est à la fois mort et vif).

    Et l'utilisation d'une jonction entre deux motifs de regex marche en Perl 6:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    # Exemple Perl 6
    > say "OK" if "toto" ~~ /^t.tu/ | /^tot./;
    OK
    >
    Certes, cela ne marche naturellement pas en Perl 5, mais l'intention du développeur est loin d'être aussi étrange que cela, puisque son code marcherait presque en Perl6 (à des nuances de syntaxe près).

  13. #13
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par caramon _majere Voir le message
    je testerais ce soir le module, je ne peux pas le télécharger au travail.
    Le module est installé par défaut avec Perl. Tu n'as pas besoin d'installer quoi que ce soit.

  14. #14
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Points : 12 469
    Points
    12 469
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par caramon _majere Voir le message
    J'utilise indifféremment les valeurs par défaut et des fois des nommés pour les retrouver dans le script car à part gagner quelque secondes à taper sur le clavier, je ne voyais pas de différences notable.
    J'utilise aussi les deux. Il y a des cas où une variable explicite est peut-être plus claire (et évite des bugs dans certains cas où la variable par défaut $_ est utilisée à plusieurs endroits dans le code, par exemple dans des boucles imbriquées), mais il y a aussi des cas, comme ici, où l'utilisation implicite de $_ simplifie notablement le code.

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

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