FAQ PerlConsultez toutes les FAQ

Nombre d'auteurs : 18, nombre de questions : 250, dernière mise à jour : 29 octobre 2015  Ajouter une question

 

Bienvenue sur la FAQ Perl. Cette FAQ a pour vocation de vous enseigner ou de vous faire revoir les notions élémentaires de ce fantastique langage. Perl est très utilisé dans différents domaines depuis la gestion système, le réseaux, l'administration de bases de données, le web (CGI), la bureautique, la conception d'interfaces graphiques ou des contextes scientifiques telle la bioinformatique. Nous espérons que cette FAQ vous sera d'une grande utilité.

Vous souhaitez participer à l'amélioration de cette FAQ, n'hésitez pas !! Commentez

Bonne lecture !


SommaireDébogage sous Perl (14)
précédent sommaire
 

La toute première chose à faire, avant quoi que ce soit, c'est d'assurer que vous vous donnez un grand service en demandant à Perl de vous aider à trouver des problèmes éventuels dans votre code. En utilisant le mode strict et les warnings, vous demandez au compilateur et à l'interpréteur Perl de détecter au plus vite pour vous d'éventuelles erreurs, ce qui peut vous faire gagner un temps précieux. Vous devez utiliser les pragmas strict et warnings pour tout programme Perl de plus d'une ligne :

Code perl : Sélectionner tout
1
2
3
4
  
#!/usr/bin/perl 
use strict; 
use warnings;

Ensuite, la méthode la plus simple et la plus commune pour déboguer un programme, dans pratiquement n'importe quel langage de programmation existant, est d'afficher à l'écran (ou parfois d'imprimer dans un fichier) le contenu des variables, le déroulement d'un programme (tracer les appels de fonctions ou de méthodes, les choix faits par le programme dans les conditions de type if ... elsif ... else, les entrées dans les boucles et le nombre d'itérations, etc.), la valeur de retour des appels de fonctions externes ou systèmes, etc. Par exemple, pour connaître la valeur d'une variable à un moment donné :

Code perl : Sélectionner tout
1
2
  
print "Le contenu de la variable value est : $value \n";

Toutefois, il se peut que la variable $value contienne au début ou surtout à la fin des caractères invisibles (espaces blancs, tabulations, retours à la ligne) qui peuvent être à l'origine de bogues assez coriaces à identifier. Il est donc généralement préférable "d'encadrer" la variable avec des caractères permettant de détecter la présence de ces caractères invisibles. Par exemple avec des crochets :

Code perl : Sélectionner tout
1
2
3
4
  
my $value = "toto  "; 
# ... 
print "Le contenu de la variable value est : [$value] \n"; # imprime : "Le contenu de la variable value est : [toto  ]".

Cette fois, les espaces invisibles à la fin de la variable sont détectables et expliquent un dysfonctionnement d'une comparaison.

Dans le cas de variables contenant des structures de données plus complexes (tableaux, tables de hachage, tableaux de hachages, hachages de tableaux, etc.), le module Data::Dumper permet d'afficher visuellement la structure de données :

Code perl : Sélectionner tout
1
2
3
use Data::Dumper; 
# ... 
print "Le contenu du hachage est: ", Dumper( \%hachage ), "\n";

Perl est livré avec un débogueur symbolique interactif qui permet de dérouler le fonctionnement d'un programme instruction par instruction (pas à pas), de poser des points d'arrêt ou de surveiller le changement d'une variable, de lire à n'importe quel moment le contenu d'une variable. Pour invoquer ce débogueur, il suffit d'utiliser l'option -d de la ligne de commande :

Code : Sélectionner tout
1
2
 
perl -d mon_programme.pl
Beaucoup de programmeurs, y compris des développeurs expérimentés parfois, semblent redouter l'utilisation d'un débogueur (quel que soit le langage) en pensant que c'est un instrument du passé qui date de l'époque où l'on écrivait ou assemblait des programmes cryptiques en C. Ils ont bien tort, parce qu'au moins le débogueur Perl est vraiment très facile à utiliser. Et il est extraordinairement puissant pour résoudre les bogues particulièrement difficiles à analyser.

Le débogueur Perl permet aussi d'ouvrir des sessions Perl interactives permettant de vérifier le bon fonctionnement de commandes complexes, ou de tester en direct des expressions régulières alambiquées. Rien que pour cela, apprendre à utiliser le débogueur est un vrai plus. On peut tester en direct, à la ligne de commande, la ligne de programme que l'on envisage d'utiliser. Le gain de temps peut être considérable.

Nous nous arrêterons ici pour cette première question, mais la plupart des questions abordées ci-dessus seront traitées plus en détail. D'autres techniques pourront être abordées en supplément ultérieurement.

Mis à jour le 22 juin 2014 Lolo78

Le pragma use strict; interdit certaines constructions syntaxiques jugées dangereuses : le compilateur échoue lorsqu'il rencontre ces genres de constructions (le programme ne peut même pas être lancé).

Il est presque toujours utile d'interdire ces constructions dangereuses, si bien qu'il est fortement recommandé de mettre ce pragma en tête de tous vos programmes (sauf éventuellement pour les scripts unilignes).

Il y a trois types de constructions rendues interdites par l'utilisation du pragma use strict;: celles relatives aux variables ('vars'), celles relatives aux fonctions ('subs') et celles relatives aux références ('refs'). Il est possible d'interdire ces types de constructions de façon individuelle et d'en autoriser d'autres. Par exemple, si l'on désire interdire seulement les constructions relatives aux fonctions, on utilisera le pragma use strict 'subs';. Lorsque l'on utilise seulement use strict;, on interdit les trois types de constructions sans distinction. Donc :

Code perl : Sélectionner tout
use strict;

est équivalent à :

Code perl : Sélectionner tout
1
2
3
use strict "vars"; 
use strict "refs"; 
use strict "subs";

Dans la très grande majorité des cas, on utilisera simplement use strict;, parce que l'on désire généralement interdire les trois types de constructions. À condition d'avoir une très bonne raison de le faire (et de très bien savoir ce que l'on fait), il est possible de désactiver temporairement l'une de ces interdictions en utilisant par exemple :

Code perl : Sélectionner tout
1
2
3
4
5
use strict; # en début de programme 
# ... du code Perl 
no strict "refs"; 
# une ou deux lignes de code utilisant des références symboliques 
use strict "refs";

Désactiver ainsi l'une des interdictions permet dans certains cas de faire des constructions "magiques", mais ce n'est pas recommandé en-dehors de cas exceptionnels et il y a presque toujours moyen de faire autrement.

  • strict "vars" : interdit les variables qui n'ont pas été déclarées (avec les fonctions my, our ou state) avant d'être utilisées) :

    Code perl : Sélectionner tout
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    use strict "vars"; 
      
    my $toto = 5;       # OK, à cause de my 
    our $titi = "titi"; # OK à cause de our 
    $tutu = "tutu";     # ne marche pas parce que $tutu n'a pas été déclaré 
    $MonPackage::i = 1  # OK, parce que le nom de la variable est donné en entier 
                        # avec le paquetage auquel elle appartient 
    my @tableau = 1..10; 
    my $compte_elements = scalar @tableau; # compte les éléments de @tableau 
    print "$compte_element \n"; # erreur de compilation : erreur sur le nom de la variable

    Comme on le voit sur le dernier exemple, use strict 'vars'; permet notamment de détecter d'éventuelles erreurs (assez fréquentes) sur le nom des variables.

    Les variables spéciales $a et $b utilisées par la fonction sort sont exemptées de cette obligation de déclaration préalable.


  • strict "refs" : interdit l'utilisation (volontaire ou non) des références symboliques (qui étaient utiles en Perl 4, mais n'ont plus de raison d'être employées avec les références "dures" et les structures de données complexes de Perl 5) :

    Code perl : Sélectionner tout
    1
    2
    3
    4
    5
    6
    7
    8
    use strict "refs"; 
    my $toto = 10; 
    $ref = \$toto; 
    print $$ref;	# ok, imprime le contenu de $toto, donc 10 
    our $titi = 20; 
    $ref = "titi"; 
    print $$ref;	# erreur à l'exécution, car c'est une référence symbolique. 
                    # Imprimerait le contenu de $titi, donc 20, sans le strict vars

  • strict "subs" : interdit l'utilisation de mots nus (barewords) pour les identifiants autres que ceux des fonctions (les mots nus restent toutefois autorisés pour les clefs des hachages s'ils sont encadrés par des { et }, ou à gauche de la "virgule grasse" =>) :

    Code perl : Sélectionner tout
    1
    2
    3
    4
    use strict "subs"; 
    my %mois = (un => "janvier", deux = "février"); # OK 
    $mois{trois} = mars;    # erreur : mars est un mot nu 
    $mois{quatre} = "avril";  # OK, en raison des guillemets autour d'avril

Mis à jour le 22 juin 2014 Lolo78

Les warnings (ou "avertissements") ont un rôle analogue au mode strict, en ce sens qu'ils préviennent le développeur de constructions dangereuses ou d'erreurs possibles de codage, mais la différence essentielle est qu'ils n'empêchent pas le programme de compiler et de s'exécuter : le programme affiche l'avertissement, mais continue cependant son exécution. Dans certains cas, le résultat du programme sera néanmoins correct, mais l'avertissement est généralement le signe que quelque chose ne fonctionne pas correctement. Il est donc fortement recommandé d'en examiner de près la raison et de l'éliminer si possible. La bonne pratique veut que du code mis en production ne devrait plus produire d'avertissement, en sorte que si un avertissement se produit quand même, c'est parce que l'on a rencontré une situation exceptionnelle.

Autrefois, le seul contrôle possible sur les avertissements était l'utilisation de l'option -w de la ligne de commande (ou de la variable $^W). Malgré son utilité, cette solution (toujours possible) présente le défaut du tout ou rien : soit on active tous les avertissements, soit on n'en active aucun. En outre, l'option -w présente l'inconvénient d'activer les avertissements sur l'ensemble du code exécuté, y compris par exemple dans des modules externes appelés par le programme principal et pas nécessairement prévus pour fonctionner avec l’intégralité des avertissements. Il en résulte qu'il est très souhaitable de ne plus utiliser l'option -w et de lui préférer l'utilisation du pragma use warnings; qui présente l'avantage d'offrir un contrôle beaucoup plus précis. Elle permet en particulier :

  • l'activation (ou la désactivation) de certains warnings et pas d'autres ;


  • l'activation (ou la désactivation) de warnings locale à un bloc de code grâce à la portée lexicale des warnings.


Les warnings signalent un nombre assez élevé (une bonne quarantaine) de constructions fautives, dangereuses ou suspectes qu'il est impossible de décrire en détail ici (une liste peut être trouvée sur ce document). Parmi les warnings les plus fréquemment rencontrés, on peut citer :

  • Use of uninitialized value $count in print at <nom_programme> line <numéro de la ligne de code> : le programme essaie de lire une variable dont le contenu n'a pas été défini (ou qui n'est plus dans la portée lexicale) ; si le programme a lu un fichier, le message indique aussi la ligne à laquelle il est arrivé du dernier fichier lu ;


  • Argument "2a" isn't numeric in addition (+) at ... : on a essayé de faire une addition dans laquelle l'une des variables contenait un argument non numérique (ici "2a") ;


  • Argument "toto" isn't numeric in numeric eq (==) at ... : l'opérateur de comparaison numérique a été utilisé avec un argument non-numérique ;


  • Name "main::count" used only once: possible typo at ... : la variable $count n'est utilisée qu'une seule fois, alors qu'en principe, une variable est utilisée au moins deux fois : la première fois quand on l'affecte et la seconde quand on utilise son contenu. Peut-être y a-t-il une faute de frappe quelque part sur le nom de la variable ;


  • "my" variable $count masks earlier declaration in same scope at ... : le programme déclare deux fois la même variable lexicale dans la même portée ;


  • readline() on closed filehandle $FH at ...: tentative de lecture sur un descripteur de fichier (filehandle) non ouvert. L'ouverture du fichier a peut-être échoué, ou le code exécuté n'est plus dans la portée lexicale du descripteur ;


  • print() on closed filehandle $FH at ... : tentative d'écriture sur un descripteur de fichier non ouvert.


Toutes les constructions signalées par des warnings ne sont pas réellement fautives ; dans certains cas, le compilateur ou l'interpréteur signale simplement qu'il soupçonne une erreur ou un danger. S'il est vraiment exceptionnel (et très rarement recommandé) de devoir désactiver l'un des pragmas use strict;, il est nettement plus courant (même si cela reste relativement rare) de désactiver un warning particulier pour un bloc de code donné. Considérons par exemple le bout de code suivant qui maintient un compteur quelconque pour chacun des douze mois d'une année :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
use warnings; 
my @months = (); 
while (my $line = <>) { 
     my $mois = (split /;/, $line)[4]; 
     $months[$mois]++; 
} 
print "$_: $months[$_] \n" foreach 1..12;
Si tous les mois ont été vus au moins une fois, les douze compteurs ont été initialisés silencieusement à 0 avant d'être incrémentés la première fois, et tout fonctionnera correctement. Si en revanche aucune activité n'a été détectée pour par exemple le mois de juillet (ce sont les vacances), le programme affichera un avertissement de ce type (le libellé exact de l'avertissement peut changer d'une version de Perl à une autre) :

Code : Sélectionner tout
Use of uninitialized value in concatenation (.) or string at ...
Le warning "Use of uninitialized value..." est certainement le plus fréquent de tous les avertissements. On peut éventuellement décider de le réduire au silence en utilisant le pragma no warnings "uninitialized"; juste avant le print de la dernière ligne du script ci-dessus (ou dans un bloc créant une portée lexicale). Mais cela n'est pas très souhaitable. Il est sans doute, dans un tel cas, préférable d'initialiser à 0 chacun des douze compteurs avec par exemple la ligne suivante :

Code perl : Sélectionner tout
$months[$_] = 0 for 1..12;
ou encore de n'imprimer la ligne que si la valeur est définie :

Code perl : Sélectionner tout
1
2
3
for my $mois (1..12) { 
     print "$mois: $months[$mois] \n" if defined $months[$mois] 
}
On voit qu'il est souvent possible d'éliminer les warnings de façon applicative, ce qui est dans ce cas la meilleure solution. Il y a cependant des cas pour lesquels il faut réellement les réduire au silence si l'on veut les éviter. Considérons par exemple ce programme utilisant une procédure récursive pour calculer la somme des entiers positifs inférieurs ou égaux à la valeur passée en paramètre :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
use strict; 
use warnings; 
  
my $seed = shift; 
print somme($seed), "\n"; 
  
sub somme { 
     my $val = shift; 
     return $val if $val < 2; 
     return $val + somme ($val - 1); 
}
En appelant ce programme avec la valeur 99, on obtient le résultat suivant :

Code : Sélectionner tout
1
2
$ perl recursive_sum.pl 99 
4950
Mais en appelant le même programme avec la valeur 100 (la somme des 100 premiers entiers), on obtient ce qui suit :
Code : Sélectionner tout
1
2
3
$ perl recursive_sum.pl 100 
Deep recursion on subroutine "main::somme" at recursive_sum.pl line 10. 
5050
Le résultat reste correct : la somme des 100 premiers entiers est bien 5050, on peut la vérifier en calculant avec la formule mathématique : (100 x 101)/2. Nous obtenons un avertissement de récursion profonde très utile dans certains cas, mais un peu gênant ici. Pourquoi ? Parce qu'il a été jugé qu'un niveau de récursion supérieur ou égal à 100 méritait un avertissement : Perl veut nous prévenir qu'il se peut que nous ayons fait une erreur et que le programme soit entré dans une récursion infinie. Pourtant, ce programme fonctionne parfaitement pour des valeurs en entrée bien plus élevées :

Code : Sélectionner tout
1
2
3
$ perl recursive_sum.pl 1000000 
Deep recursion on subroutine "main::somme" at recursive_sum.pl line 10. 
500000500000
Si nous savons que cela fonctionne pour une valeur en entrée de un million, et si nous avons par ailleurs l'assurance que les valeurs en entrée ne dépasseront jamais un million, nous pouvons réduire au silence l’avertissement sur la récursion profonde (en gardant les autres warnings actifs) :
Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
use strict; 
use warnings; 
  
my $seed = shift; 
die "Valeur en entrée trop élevée" if $seed > 1E6; # se prémunir contre les valeurs en entrée trop élevées 
print somme($seed), "\n"; 
  
sub somme { 
     my $val = shift; 
     return $val if $val < 2; 
     {    no warnings 'recursion'; 
          return $val + somme ($val - 1); 
     } 
}
Nous avons mis le pragma no warnings 'recursion'; et l'appel récursif dans un bloc lexical particulier (les accolades { et }) pour montrer, de façon générale, comment réduire la portée du pragma à la seule ligne de code pour laquelle c'était nécessaire, même si créer un tel bloc n'était dans ce cas précis pas nécessaire, car la portée du pragma no warnings 'recursion'; se serait arrêtée de toute façon à l'accolade fermant la définition de la fonction. Il était donc ici suffisant d'écrire la fonction somme comme suit:

Code perl : Sélectionner tout
1
2
3
4
5
6
sub somme { 
     my $val = shift; 
     return $val if $val < 2; 
     no warnings 'recursion'; 
     return $val + somme ($val - 1); 
}

La recommandation est donc la suivante : utilisez toujours le pragma use warnings; (au moins pour tous les programmes de plus d'une ligne de code) et examinez attentivement les avertissements émis par Perl. Ces derniers signifient souvent qu'il y a quelque chose d'erroné dans votre code, ils constituent une aide puissante au débogage et vous feront gagner un temps considérable. Il peut cependant arriver (rarement) qu'il faille réduire une catégorie d'avertissement au silence. Ne le faites que s'il n'y a pas moyen de faire autrement et si vous savez vraiment ce que vous faites ; et dans ce cas, ne le faites que pour le seul avertissement concerné et dans une portée lexicale aussi limitée que possible.

Mis à jour le 29 juin 2014 Lolo78

Les warnings ne sont pas toujours d'une grande clarté. Considérons par exemple le script uniligne suivant (qui ne fait rien de bien utile, mais c'est pour l'exemple) :

Code : Sélectionner tout
1
2
3
4
5
6
 
$ perl -e 'use strict; use warnings; my %hash = (jan => 1, fev = 2); 
           print "true" if defined %hash;' 
defined(%hash) is deprecated at -e line 1. 
        (Maybe you should just omit the defined()?) 
true
Le message dit que l'utilisation du bout de code defined(%hash) est déprécié et suggère une correction possible : omettre le defined(). On peut faire ce qui est suggéré, mais on peut aussi souhaiter mieux comprendre ce qui ne va pas dans ce code.

L'utilisation du pragma use diagnostics; permet alors d'avoir un libellé d'avertissement plus explicite :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
 
$ perl -e 'use strict; use warnings; use diagnostics;  
           my %hash = (jan => 1, feb => 2,);  
           print "true" if defined %hash;' 
defined(%hash) is deprecated at -e line 1 (#1) 
    (D deprecated) defined() is not usually useful on hashes because it 
    checks for an undefined scalar value.  If you want to see if the hash 
    is empty, just use if (%hash) { # not empty } for example. 
 
        (Maybe you should just omit the defined()?) 
true
C'est bien plus clair. Nous savons maintenant que si nous voulons vérifier que le hash n'est pas vide, il suffit d'utiliser ... if %hash; au lieu de ... if defined %hash;. Ce qui donne :

Code : Sélectionner tout
1
2
3
4
$ perl -e 'use strict; use warnings; use diagnostics; 
                 my %hash = (jan => 1, feb => 2,);  
                 print "true" if %hash;' 
true
De même, l'ajout du pragma use diagnostics; à la première version du programme récursif de la question précédente donne un message beaucoup plus explicite :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
$ perl recursive_sum.pl 100 
Deep recursion on subroutine "main::somme" at recursive_sum.pl line 13 (#1) 
    (W recursion) This subroutine has called itself (directly or indirectly) 
    100 times more than it has returned.  This probably indicates an 
    infinite recursion, unless you're writing strange benchmark programs, in 
    which case it indicates something else. 
 
    This threshold can be changed from 100, by recompiling the perl binary, 
    setting the C pre-processor macro PERL_SUB_DEPTH_WARN to the desired value. 
 
5050
Ce message donne un bon nombre d'indications supplémentaires très utiles.

Mis à jour le 29 juin 2014 Lolo78

Considérons les deux scripts unilignes suivants :

Code : Sélectionner tout
1
2
3
4
$ echo toto | perl -e ' while (<>) { print "true\n" if $_ eq "toto"}' 
 
$ echo toto | perl -e ' while (<>) { print "true\n" unless $_ eq "toto"}' 
true
La commande shell echo toto affiche normalement "toto" à l'écran, mais la sortie est redirigée vers un script Perl par un pipe Unix. Ce script lit l'entrée standard et affiche "true" si la ligne lue est égale à "toto" (premier script) ; et si elle n'est pas égale à "toto" (second script), rien de bien compliqué, sauf que ça ne marche pas comme prévu. On s'attend à ce que le premier script affiche "true" et pas le second, mais c'est l'inverse qui se produit.

Dans ce genre de cas, l'une des premières choses à faire est d'afficher le contenu des variables concernées en prenant soin de les encadrer dans des caractères spéciaux (tels que des crochets [ et ] ou des signes < et >) pour être sûr de visualiser l'ensemble de la variable (en détectant d'éventuels caractères invisibles). Ici, il faut par exemple ajouter une instruction print "[$_]\n"; pour afficher le contenu de la ligne lue (stockée dans la variable $_ par défaut) :

Code : Sélectionner tout
1
2
3
$ echo toto | perl -e ' while (<>) { print "[$_]\n"; print "true\n" if $_ eq "toto"}' 
[toto 
]
On voit que le crochet fermant est à la ligne, ce qui signifie que la ligne lue contient non seulement "toto", mais aussi un retour à la ligne. Du coup, elle n'est pas égale à la chaîne "toto", mais à la chaîne "toto\n". Une fois la source de l'erreur connue, la correction est très facile, il suffit par exemple de faire un chomp pour élaguer le retour à la ligne à la fin de la variable. Nos deux scripts unilignes fonctionnent maintenant comme prévu :

Code : Sélectionner tout
1
2
3
4
$ echo toto | perl -e ' while (<>) { chomp $_; print "true\n" if $_ eq "toto"}' 
true 
 
$ echo toto | perl -e ' while (<>) { chomp; print "true\n" unless $_ eq "toto"}'
Dans presque tous les langages de programmation, imprimer le contenu des variables est la première méthode à utiliser quand quelque chose ne fonctionne pas comme prévu. En Perl, l'ajout des crochets (ou tout autre signe, un tiret par exemple) permet de détecter la présence de caractères invisibles.

Mis à jour le 29 juin 2014 Lolo78

Une question précédente recommande d'utiliser la fonction print pour afficher le contenu d'une variable ou toute autre information sur le déroulement du programme. Cette recommandation était une simplification volontaire, il est parfois utile d'utiliser d'autres fonctions.

Il est parfois préférable d'utiliser la fonction warn qui présente la particularité d'écrire ses messages de débogage sur la sortie d'erreur (STDERR) et non sur la sortie standard (STDOUT). L'avantage ? Supposons que sous Unix, par exemple, nous ayons redirigé la sortie standard vers un fichier (ou vers un autre programme à l'aide d'un pipe) avec une ligne de commande de ce type :

Code : Sélectionner tout
1
2
 
% perl mon_programme.pl input.txt > output.txt
ou:
Code : Sélectionner tout
1
2
 
% perl mon_programme.pl input.txt | perl autre_programme.pl
Dans ce cas, les messages affichés à l'aide de la fonction print se retrouveront dans le fichier en sortie (ou dans l'entrée de l'autre programme), ce qui posera sans doute des problèmes, et surtout, ne sera sans doute pas détecté tout de suite. En utilisant warn, les messages de débogage sont dirigés sur la sortie d'erreur et seront, dans les exemples ci-dessus, affichés à l'écran.

Le module standard Carp permet un affichage plus utile, notamment pour les auteurs de modules. Il permet d'indiquer à l'utilisateur non seulement le numéro de la ligne de code où il y a eu un problème, mais aussi la fonction (et la ligne de code) qui l'a appelé, ce qui est souvent plus utile à l'utilisateur du module. Deux fonctions du module Carp sont à peu près équivalentes à un warn : carp et cluck. Voici un exemple très simple d'utilisation :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use strict; 
use warnings; 
use Carp qw/carp cluck/; 
  
my $val = 5; 
a($val); 
sub a { 
	my $arg = shift; 
	b($arg*2); 
} 
sub b { 
	c(3 * shift); 
} 
sub c { 
	my $d = shift; 
	carp "erreur sur la variable $d" if $d > 25; 
	print "La valeur obtenue est : $d \n"; 
}

La fonction a est appelée avec le paramètre $val (5). Elle appelle la fonction b et lui passe en argument le double de la valeur reçue (donc 10). La fonction b appelle la fonction c et lui passe le triple de la valeur reçue en paramètre (donc 30). L'exécution produit l'affichage suivant :

Code : Sélectionner tout
1
2
3
4
5
erreur sur la variable 30 at test_carp.pl line 16 
        main::c(30) called at test_carp.pl line 12 
        main::b(10) called at test_carp.pl line 9 
        main::a(5) called at test_carp.pl line 6 
La valeur obtenue est : 30
On voit que carp permet d'afficher tout l'arbre des appels de fonctions ayant conduit à l'anomalie. C'est beaucoup plus parlant pour comprendre ce qui s'est passé (et comment il se fait que la variable $d ait atteint une valeur supérieure à la limite de 25). À noter que carp émet seulement un avertissement (comme warn), mais n'interrompt pas l'exécution du programme (qui se poursuit jusqu'à la fin et affiche bien le message de fin). Avec ce programme très simple, la fonction cluck afficherait à peu près la même chose, mais dans certains cas plus complexes, elle peut fournir un affichage plus détaillé. Ce module propose deux autres fonctions, croak et confess, qui font à peu près la même chose, mais entraînent la fin de l'exécution du programme (comme la fonction die).

Mis à jour le 26 juillet 2014 Lolo78

Imaginons que nous voulions imprimer le contenu d'un tableau. Si on imprime directement la variable :

Code perl : Sélectionner tout
1
2
3
4
5
use strict; 
use warnings; 
  
my @tableau = qw / janvier fevrier mars avril mai juin /; 
print @tableau, "\n";

le résultat n'est pas très lisible :

Code : Sélectionner tout
1
2
$ perl print_tab.pl 
janvierfevriermarsavrimmaijuin
Il suffit cependant de mettre la variable entre guillemets pour que Perl nous mette obligeamment un séparateur entre les différents éléments. Le séparateur de listes est contenu dans la variable spéciale $" et vaut par défaut un espace. Remplaçons la ligne d'affichage ci-dessus par celle-ci :

Code perl : Sélectionner tout
print "@tableau \n";

pour que le résultat soit bien plus satisfaisant :

Code : Sélectionner tout
1
2
$ perl print_tab.pl 
janvier fevrier mars avril mai juin
Cette solution est bien suffisante à condition que la liste soit relativement courte.

Il suffit de modifier la variable spéciale $" pour obtenir un séparateur différent. Par exemple, pour afficher chaque élément du tableau sur une ligne séparée, on peut donner à cette variable la valeur d'un retour à la ligne :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
use strict; 
use warnings; 
  
my @tableau = qw / janvier fevrier mars avril mai juin /; 
{ 
	local $" = "\n"; 
	print "@tableau \n"; 
}

Ce qui affiche :
Code : Sélectionner tout
1
2
3
4
5
6
7
 
janvier 
fevrier 
mars 
avril 
mai 
juin
Ici, nous avons localisé la variable $" dans un bloc lexical pour éviter que sa modification puisse avoir des effets de bords fâcheux ailleurs dans le programme. Cela n'est pas forcément indispensable quand il s'agit d'une instruction qui est ajoutée provisoirement au cours d'une séance de débogage et qui sera retirée tout de suite après, mais c'est toujours une bonne chose de prendre ses précautions.

Cette variable spéciale $" peut contenir plusieurs caractères, par exemple une virgule et un espace. Le bloc lexical définissant localement la variable $" devient :

Code perl : Sélectionner tout
1
2
3
4
{ 
	local $" = ", "; 
	print "@tableau \n"; 
}

ce qui donne :
Code : Sélectionner tout
1
2
 
janvier, fevrier, mars, avril, mai, juin
Bien souvent, surtout si le nombre d'éléments est plus élevé, on peut souhaiter voir non seulement les éléments du tableau, mais aussi l’indice permettant d'y accéder. On pourrait facilement faire ce qui suit :
Code perl : Sélectionner tout
1
2
3
4
5
use strict; 
use warnings; 
  
my @tableau = qw / janvier fevrier mars avril mai juin /; 
print "$_ : $tableau[$_] \n" for 0..$#tableau;
ce qui imprime à l'écran :
Code : Sélectionner tout
1
2
3
4
5
6
0 : janvier 
1 : fevrier 
2 : mars 
3 : avril 
4 : mai 
5 : juin
Le module standard Data::Dumper permet d'afficher de façon propre, claire et nette la plupart des structures de données (tableaux, hachages et structures composites plus complexes telles des tableaux de tableaux, hachages de tableaux, etc.). Avec le tableau précédemment utilisé, il suffit de faire un use Data::Dumper; et de passer à la fonction print la valeur de retour de la fonction Dumper sur une référence pointant vers ce tableau :

Code perl : Sélectionner tout
1
2
3
4
5
6
use strict; 
use warnings; 
use Data::Dumper; 
  
my @tableau = qw / janvier fevrier mars avril mai juin /; 
print Dumper \@tableau;

Ce qui donne :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
$VAR1 = [ 
          'janvier', 
          'fevrier', 
          'mars', 
          'avril', 
          'mai', 
          'juin' 
        ];
Supposons que nous voulions deux hachages : un faisant le lien entre le mois et son numéro dans l'année, un autre faisant le lien en sens inverse. Comme nous sommes paresseux, nous allons utiliser notre liste ordonnée de mois pour construire ces hachages de façon légèrement astucieuse. Nous voulons vérifier que la technique utilisée donne bien les deux hachages voulus.

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
use strict; 
use warnings; 
use Data::Dumper; 
  
my @tableau = qw / janvier fevrier mars avril mai juin /; 
my $iter = 1; 
my %hachage_1 = map {$_, $iter++} @tableau; 
$iter = 1; 
my %hachage_2 = map {$iter++, $_} @tableau; 
print Dumper \%hachage_1, \%hachage_2;

L'affichage montre que cette technique a bien fonctionné :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$VAR1 = { 
          'mai' => 5, 
          'juin' => 6, 
          'avril' => 4, 
          'mars' => 3, 
          'fevrier' => 2, 
          'janvier' => 1 
        }; 
$VAR2 = { 
          '6' => 'juin', 
          '4' => 'avril', 
          '1' => 'janvier', 
          '3' => 'mars', 
          '2' => 'fevrier', 
          '5' => 'mai' 
        };
Voici un exemple de tableau de hachages... rassemblant quelques informations sur trois savants célèbres (la structure exacte n'est pas exactement la même selon les informations recueillies sur chacun des trois personnages).

Code perl : Sélectionner tout
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
use strict; 
use warnings; 
use Data::Dumper; 
  
my @savants = ( { Newton =>   { prenom => "Isaac",   
                                domaines => [qw /maths optique astronomie/], 
                                dates => { vie => {naissance => 1643, mort => 1727}}}}, 
                { Einstein => { prenom => "Albert",   
                                domaines => [qw /physique cosmologie/], 
                                dates => { vie => {naissance => 1879, mort => 1955}, 
                                           decouvertes => {  
                                                           "relativite restreinte" => 1905,  
                                                           quantas => 1905,  
                                                           "relativ. generale" => 1916,} 
                                         } 
                               } 
                }, 
                { Pasteur =>  { prenom => "Louis", 
                                domaines => [qw /chimie cristallographie medecine/], 
                                dates => { vie => {naissance => 1822, mort => 1895}, 
                                           decouvertes => { 
                                                           "dissymetrie moleculaire" => 1848, 
                                                           fermentation => "1857-67", 
                                                           pasteurisation => "1863-66", 
                                                           "microbes et vaccins" => "1876-1890" 
                                                        } 
                                         } 
  
                              } 
                } 
              ); 
print Dumper \@savants;

La structure est assez complexe, l'appel à Dumper permet de vérifier si elle est correcte, ce qui est le cas :

Code : Sélectionner tout
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
$VAR1 = [ 
          { 
            'Newton' => { 
                          'prenom' => 'Isaac', 
                          'dates' => { 
                                       'vie' => { 
                                                  'mort' => 1727, 
                                                  'naissance' => 1643 
                                                } 
                                     }, 
                          'domaines' => [ 
                                          'maths', 
                                          'optique', 
                                          'astronomie' 
                                        ] 
                        } 
          }, 
          { 
            'Einstein' => { 
                            'prenom' => 'Albert', 
                            'dates' => { 
                                         'decouvertes' => { 
                                                            'quantas' => 1905, 
                                                            'relativite restreinte' => 1905, 
                                                            'relativ. generale' => 1916 
                                                          }, 
                                         'vie' => { 
                                                    'mort' => 1955, 
                                                    'naissance' => 1879 
                                                  } 
                                       }, 
                            'domaines' => [ 
                                            'physique', 
                                            'cosmologie' 
                                          ] 
                          } 
          }, 
          { 
            'Pasteur' => { 
                           'prenom' => 'Louis', 
                           'dates' => { 
                                        'decouvertes' => { 
                                                           'microbes et vaccins' => '1876-1890', 
                                                           'pasteurisation' => '1863-66', 
                                                           'fermentation' => '1857-67', 
                                                           'dissymetrie moleculaire' => 1848 
                                                         }, 
                                        'vie' => { 
                                                   'mort' => 1895, 
                                                   'naissance' => 1822 
                                                 } 
                                      }, 
                           'domaines' => [ 
                                           'chimie', 
                                           'cristallographie', 
                                           'medecine' 
                                         ] 
                         } 
          } 
        ];
Le module Data::Dumper a l'avantage d'être un module standard et d'être disponible sur n'importe quelle distribution un tant soit peu récente de Perl. À noter qu'il existe des modules additionnels (Data::Dumper::Simple, Data::Dumper::Concise, etc.) permettant des affichages simplifiés ou moins encombrants, mais ces derniers devront être installés séparément.

Un autre outil intéressant pour ce genre d'affichage est le module Data::Dump qui fournit un affichage plus compact.

Mis à jour le 2 juillet 2014 Lolo78

Plusieurs méthodes permettent d'éviter des impressions intempestives de messages lorsque l'on ne débogue plus. La plus simple est de n'imprimer des messages de débogage que si une variable booléenne est vraie. Par exemple:

Code perl : Sélectionner tout
1
2
3
4
5
6
7
  
use strict;  
use warnings;  
my $DEBUG = 1; # mettre $DEBUG à 1 quand on débogue et à 0 quand on ne débogue plus. 
  
# ... plus loin 
print "Valeur de variable: [$variable] \n" if $DEBUG;

Quand le programme est au point, il suffit de changer une seule ligne du programme pour désactiver les impressions de débogage :

Code perl : Sélectionner tout
1
2
  
my $DEBUG = 0; # mettre $DEBUG à 1 quand on débogue et à 0 quand on ne débogue plus.

Il est toutefois généralement préférable d'utiliser une constante plutôt qu'une variable :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
  
use strict;  
use warnings;  
use constant DEBUG => 1; # mettre DEBUG à 1 quand on débogue et à 0 quand on ne débogue plus. 
  
# ... plus loin 
print "Valeur de variable: [$variable] \n" if DEBUG;
On peut également utiliser une fonction de débogage pour que toutes les instructions de débogage soient traitées au même endroit :
Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
  
use strict;  
use warnings;  
use constant DEBUG => 1; # mettre DEBUG à 1 quand on débogue et à 0 quand on ne débogue plus. 
  
# ... plus loin 
log ("Valeur de variable: [$variable]"); 
#... 
sub log {  
     return unless DEBUG; 
     my $msg = shift; 
     print $msg, "\n"; 
}
Dans certains cas, il peut être préférable d'écrire les messages de débogage dans un fichier de compte-rendu d'exécution (fichier de log) plutôt que de les afficher à l'écran. Dans ce cas, il se peut que les messages de débogage ne gênent personne.

Si vous n'avez plus que quelques impressions dans un fichier, vous avez sans doute intérêt à utiliser le module Log::Log4perl (également disponible sur le CPAN). Ce module est d'une grande richesse et ne peut être résumé en quelques lignes. Il définit notamment plusieurs niveaux de débogage (DEBUG, INFO, WARN, ERROR et FATAL) permettant de contrôler la quantité de messages imprimés dans le compte-rendu.

Un autre module très utile est le Smart::Comments. Quand on utilise ce module, tous les commentaires commençant par au moins trois signes dièse (###) deviennent des instructions de débogage et ce qu'il y a dans le commentaire est affiché sur la sortie d'erreur (STDERR). Le premier exemple ci-dessus peut être réécrit comme suit :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
  
use strict;  
use warnings;  
use Smart::Comments; # rend "magiques" les ### 
  
# ... plus loin 
### Valeur de variable: [$variable];

La ligne 7 sera affichée à l'écran comme si on avait le print dans le code.

Quand vous avez fini de déboguer, il suffit d'enlever la ligne use Smart::Comments; ou de la mettre en commentaire pour que les lignes commençant par ### redeviennent de simples commentaires et n'affectent pas le fonctionnement du programme.

Mis à jour le 14 juillet 2014 Lolo78

Parfois, le programme Perl a l'air correct, mais le compilateur Perl semble comprendre autre chose. Il est facile de demander à Perl comment il a interprété le code qui lui a été soumis.

Prenons ce petit programme erroné (dérivé d'une question posée sur un forum le jour même où nous écrivons ces lignes) :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
  
#!/usr/bin/perl 
use strict; 
use warnings; 
  
my $var = "3"; 
print $var > 2 ? "yes" : "no", "\n"; 
exit $var > 2 ? 1 : 0;

Si nous exécutons la ligne de commande suivante sous Unix ou Linux :
Code : Sélectionner tout
1
2
 
$ perl  test.pl ; echo $?
cela nous affiche le résultat suivant :
Code : Sélectionner tout
1
2
3
 
yes 
3
Que se passe-t-il ? Le print $var > 2 ? "yes" : "no" de la ligne 7 ci-dessus a bien interprété l'opérateur ternaire pour afficher "yes", mais pour la ligne suivante, l'impression du code retour (variable système $?) renvoyé par le programme au système d'exploitation affiche 3 alors que l'on pourrait attendre 1 (de même que l'on a eu "yes" avec le print).

Le module standard B::Deparse permet de décompiler la structure interne du code créée par Perl lors de la compilation du programme et d'afficher le résultat de cette décompilation sous la forme de code Perl. Essayons avec le programme ci-dessous :
Code perl : Sélectionner tout
1
2
3
4
5
6
7
$ perl -MO=Deparse,-p test.pl 
use warnings; 
use strict 'refs'; 
(my $var = '3'); 
print((($var > 2) ? 'yes' : 'no'), "\n"); 
((exit($var) > 2) ? '???' : '???'); 
test.pl syntax OK
L'option -p de Deparse demande l'ajout de parenthèses pour mieux comprendre comment le compilateur a interprété le code source. On constate trois choses :
  • le print met des parenthèses autour de l'ensemble de l'opérateur ternaire ? =, ce qui veut dire qu'il évalue l'ensemble de l'opérateur ternaire avant l'impression. D'où l'évaluation correcte à "yes" ;
  • le exit, en revanche, sort du programme en passant au système d'exploitation la valeur de $var avant d'évaluer l'opérateur ternaire (qui n'est donc jamais évalué puisque exit ne retourne pas dans le programme) ;
  • les '???' dans la dernière ligne signifient que le compilateur a optimisé (c'est-à_dire, dans ce cas, purement et simplement éliminé) les constantes 2 et 1 de l'opérateur ternaire qui ne servent à rien.

La cause du problème est maintenant claire : il y a un problème de priorité des opérateurs (operator precedence). Contrairement au print, la fonction exit évalue seulement la première expression qui le suit et sort avec cette valeur. La correction est simple : il faut forcer l'évaluation de l'opérateur ternaire avant de sortir. Par exemple, en modifiant la dernière ligne du programme comme suit :
Code perl : Sélectionner tout
1
2
  
exit ($var > 2 ? 1 : 0);
À l'exécution, on obtient bien cette fois le résultat attendu :
Code : Sélectionner tout
1
2
3
4
 
$ perl test.pl; echo $? 
yes 
1
La décompilation confirme que nous avons maintenant une bonne priorité des opérateurs :
Code perl : Sélectionner tout
1
2
3
4
5
6
7
$ perl -MO=Deparse,-p  test.pl 
use warnings; 
use strict 'refs'; 
(my $var = '3'); 
print((($var > 2) ? 'yes' : 'no'), "\n"); 
exit((($var > 2) ? 1 : 0)); 
test.pl syntax OK

Mis à jour le 15 juillet 2014 Lolo78

Quand on envisage d'utiliser une nouvelle fonction que l'on ne connaît pas encore bien, ou une option inhabituelle d'une fonction, ou une expression un peu compliquée sur des structures de données complexes, ou une expression régulière alambiquée, le mieux est souvent de la tester rapidement pour la mettre bien au point avant de l'inclure dans un programme complet où il sera peut-être plus difficile de détecter d'où vient un éventuel problème. On peut alors insérer dans le programme un bout de code déjà testé unitairement et qui aura de bien meilleures chances de fonctionner.

Plusieurs techniques sont envisageables, deux seront abordées dans la présente question (et d'autres, ailleurs dans cette FAQ).

La première a déjà été utilisée précédemment dans cette FAQ pour des exemples de code. Il s'agit du script uniligne, utilisé à des fins de test. Un script uniligne est un script que l'on écrit directement à la ligne de commande grâce à l'option -e. Voici un exemple de script uniligne ne faisant pas grand chose d'utile :

Code perl : Sélectionner tout
1
2
3
  
$ perl -e 'print "hello world!";' 
hello world!
L'option -e signale à Perl que le code à exécuter ne se trouve pas dans un fichier, mais dans la chaîne entre apostrophes qui suit 'print "hello world!";'. Le programme affiche bien la chaîne de caractère voulue. L'exemple ci-dessus est typique d'une utilisation sous Unix ou Linux, les utilisateurs de Perl sous Windows devront remplacer les guillemets par des apostrophes et réciproquement sous une fenêtre "dos" (cmd) ou Powershell :
Code : Sélectionner tout
perl -e "print 'hello world!';"
Considérons un exemple plus intéressant. Question posée le même jour où j'écris ces lignes sur un forum : comment vérifier si la chaîne $str2 est présente dans la chaîne $str1 ? L'auteur de la question utilisait la syntaxe suivante :
Code : Sélectionner tout
if($str1 =~ /$str2/gi){...
qui ne fonctionnait pas correctement (en raison du contenu de $str1 et de $str2, mais peu importe la raison, l'objet est ailleurs). Deux personnes ont répondu sur ce forum qu'il fallait inverser $str1 et $str2 pour obtenir le résultat désiré (ce qui est faux). J'ai donc répondu que c'était une erreur et que pour vérifier si $str2 faisait partie de $str1, il fallait bien que $str1 figure à gauche de l'opérateur =~ et $str2 à sa droite. Mais comme deux personnes (compétentes en Perl et connues comme telles sur le site) avaient dit le contraire, je ne pouvais me contenter de les contredire sans apporter de preuve. Il me fallait d'abord vérifier que ce n'était pas moi qui déraillais (après tout il y a moyen de se mélanger les pinceaux sur de tels trucs), et surtout fournir un contre-exemple clair montrant que ces deux personnes se trompaient. Voici l'exemple très simple que j'ai proposé pour étayer mes dires :
Code perl : Sélectionner tout
1
2
3
4
5
  
$  perl -E 'my $str1 = qq(one two three); my $str2 = qq(two); if ($str2 =~ m/$str1/) { say "Vrai";}' 
  
$  perl -E 'my $str1 = qq(one two three); my $str2 = qq(two); if ($str1 =~ m/$str2/) { say "Vrai";}' 
Vrai
En testant l'exemple dans les deux sens, on montre clairement que le second (if ($str1 =~ m/$str2/)) fonctionne correctement (il imprime "Vrai", car le mot "two" est bien inclus dans la chaîne "one two three") et que le premier ne fonctionne pas. S'il y avait un doute, nous savons maintenant de façon à peu près certaine (sous réserve d'avoir testé correctement) qu'il faut utiliser la syntaxe du second exemple.

Ce test prend trois ou quatre minutes et permet de s'assurer que l'on tient la bonne syntaxe. Si ça ne marche pas correctement avec l'exemple réel, le problème est sans doute ailleurs.

La seconde technique consiste à utiliser le débogueur Perl pour tester la réponse de Perl de façon interactive, ce que l'on peut faire avec la commande suivante à la ligne de commande :
Code : Sélectionner tout
1
2
 
$ perl -de 42
Cela ouvre une fenêtre de débogage interactive.

En reprenant le problème de l'exemple ci-dessus, il suffit de taper les lignes suivantes à l'invite du débogueur :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
 
  DB<1> $str1 = qq(one two three); 
 
  DB<2>  $str2 = qq(two); 
 
  DB<3> print "Vrai" if $str2 =~ m/$str1/; 
 
  DB<4> print "Vrai" if $str1 =~ m/$str2/; 
Vrai 
  DB<5>
Ici encore, on établit facilement la syntaxe à utiliser.

À noter un point important : quand on utilise le débogueur de façon interactive comme ci-dessus, il ne faut surtout pas déclarer les variables avec le mot-clef my, car plus rien ne marche (disons pour expliquer brièvement que la fonction my définit une portée lexicale, et que l'idée de portée lexicale n'a pas vraiment de sens quand le programme n'est pas dans un fichier, mais saisi directement à la ligne de commande de la fenêtre du débogueur).

Utiliser le débogueur pour tester Perl de façon interactive est immensément utile, une prochaine question de la FAQ donnera d'autres exemples variés.

Mis à jour le 17 juillet 2014 Lolo78

La question précédente a brièvement montré comment utiliser le débogueur Perl pour tester de façon interactive la validité d'un bout de code.

Voici quelques exemples complémentaires.

Quelques commandes de base du débogueur

Voyons d'abord quelques commandes de base pour utiliser le débogueur Perl interactivement :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ perl -de 42 
  
Loading DB routines from perl5db.pl version 1.33 
Editor support available. 
  
Enter h or `h h' for help, or `man perldebug' for more help. 
  
main::(-e:1):   42 
  DB<1> $c = "Larry wall"; # le createur de Perl 
  
  DB<2> print $c; # imprime la valeur de $c 
Larry wall 
  DB<3> p $c; # raccourci du debugger pour imprimer la valeur de $c 
Larry wall
On voit que sous le débogueur Perl, la commande p sur une variable scalaire a (à peu près) le même effet que l'utilisation d'un print. C'est essentiellement un raccourci (très utile quand on déboguera vraiment).

Les points-virgules à la fin des instructions sont inutiles, de même que les commentaires qui ne servent qu'à éclairer le lecteur sur ce qui se passe.

Continuons avec un tableau :
Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  
  DB<4>  @tableau = qw / wall christiansen schwartz chromatic conway poe /; # quelques auteurs connus de Perl 
  
  DB<5> p @tableau; # imprime les éléments du tableau mis bout à bout. C'est très laid. 
wallchristiansenschwartzchromaticconwaypoe 
  DB<6> p "@tableau"; imprimer le tableau entre guillemets permet un affichage nettement plus satisfaisant 
wall christiansen schwartz chromatic conway poe 
  DB<7> x @tableau; # afficher la structure du tableau : élément 0 = wall, élément 1 = christiansen, etc. 
0  'wall' 
1  'christiansen' 
2  'schwartz' 
3  'chromatic' 
4  'conway' 
5  'poe' 
  DB<8> x \@tableau; # x sur une référence sur un tableau affiche encore plus clairement la structure des données 
0  ARRAY(0x6005009e8) 
   0  'wall' 
   1  'christiansen' 
   2  'schwartz' 
   3  'chromatic' 
   4  'conway' 
   5  'poe'
À noter également que le code ci-dessus n'utilise pas l'opérateur my pour déclarer les variables $c et @tableau ; c'est délibéré, il ne faut pas le faire, car déclarer une portée lexicale n'a de sens que dans un fichier (ou un bout de code complet), cela ne fonctionnerait pas avec des commandes que l'on saisit au fur et à mesure.

En résumé, les deux commandes spécifiques au débogueur utilisé de façon interactive sont les suivantes :
  • p : imprime le contenu d'une variable scalaire à peu près comme le fait print $var ;
  • x : imprime le contenu d'une structure de données à peu près comme le fait print Dumper @data.


Nous pouvons maintenant tester des expressions Perl un peu plus complexes :
Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  
  DB<9> print join ", ", @tableau; # tester un bout de code un peu plus complexe, avec un join  
wall, christiansen, schwartz, chromatic, conway, poe 
  
  DB<10> print map { $_ = ucfirst $_; "$_, " }  @tableau; # un peu plus complexe : lettre capitale initiale pour ces personnes 
Wall, Christiansen, Schwartz, Chromatic, Conway, Poe, 
  DB<11> x \@tableau; # attention : le map précédent, en modifiant $_, a modifié le contenu du tableau 
0  ARRAY(0x6005009e8) 
   0  'Wall' 
   1  'Christiansen' 
   2  'Schwartz' 
   3  'Chromatic' 
   4  'Conway' 
   5  'Poe'

Explorer une structure de données complexe

Les structures de données imbriquées sont parfois un peu compliquées à manipuler. Il peut être utile de tester les bouts de code que l'on va mettre dans son programme pour vérifier que l'on récupère bien ce que l'on désire.

Considérons le tableau de hash (AoH) suivant :
Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
  
  DB<1>   @AoH = (  { jan => 1, feb => 2, mar => 3}, { apr => 4, may => 5, june => 6} ); 
  
  DB<2> x \@AoH; 
0  ARRAY(0x600500b38) 
   0  HASH(0x60005f160) 
      'feb' => 2 
      'jan' => 1 
      'mar' => 3 
   1  HASH(0x6005008e0) 
      'apr' => 4 
      'june' => 6 
      'may' => 5

Si nous prenons le premier élément du tableau, nous obtenons une référence vers un hash :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
  
  DB<3> $hashref = $AoH[0]; 
  
  DB<4> x $hashref; 
0  HASH(0x60005f160) 
   'feb' => 2 
   'jan' => 1 
   'mar' => 3

Examinons le contenu du hash :
Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
  DB<5> p $hashref{jan}; # n'affiche rien 
  
  DB<6> x  $hashref{jan}; # ne fonctionne toujours pas, $hashref{jan} n'est pas défini 
0  undef 
  DB<7> p $hashref->{jan}; # cette fois, ça marche 
1 
  DB<8> x $hashref->{jan}; # $hashref n'est pas un hash, mais une référence vers un hash. Déréférençons avec -> 
0  1
Les deux premières lignes (DB<5> et DB<6>) essayaient d'utiliser $hashref comme si c'était un hash. Cela ne fonctionne pas (parce que ce n'est pas un hash, mais une référence sur un hash). En utilisant la syntaxe $hashref->{jan}, on déréférenie le hash et affiche bien la valeur correspondant à 'jan'. Le test a pris une minute et nous savons maintenant quelle syntaxe utiliser dans le programme à écrire.

Essayons maintenant de prélever une "tranche" du hash :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
  
  DB<9> p $hashref->{"jan", "feb"}; # ne fonctionne pas 
  
  DB<10> x $hashref->{"jan", "feb"}; # toujours pas 
0  undef 
  DB<11> x @$hashref{"jan", "feb"} ; # cette fois, c'est bon 
0  1 
1  2

Nous pouvons maintenant essayer d'alimenter un tableau avec la tranche de hash :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
  
  DB<12> @valeurs =  @$hashref{ qw/ jan feb mar / }; 
  
  DB<13> x \@valeurs 
0  ARRAY(0x60052b450) 
   0  1 
   1  2 
   2  3
On peut maintenant revenir au AoH d'origine et extraire toutes les valeurs des tables de hachage imbriquées :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
  
  DB<14> for $href (@AoH) { push @allvalues, sort values %$href}; 
  
  DB<15> x @allvalues 
0  1 
1  2 
2  3 
3  4 
4  5 
5  6
Écrire correctement d'emblée la ligne de code [14] aurait sans doute demandé une bonne (voire très bonne) expérience des structures complexes et des références, le débogueur a permis de tester les morceaux de code et d'arriver à la syntaxe voulue sans difficulté.

Exemple avec une expression régulière

Essayons d'extraire le contenu d'une chaîne de caractères dont nous connaissons le premier et le dernier mot (et disons que nous voulons le contenu à l'exception de ces premier et dernier mots) :
Code : Sélectionner tout
1
2
3
4
5
6
 
  DB<1>  $string = "Une chaine avec \n un retour à la ligne"; 
 
  DB<2>  print $1 if $string =~ /Une (.+)ligne/ 
 
  DB<3>  p $1
Mince, pourquoi cette regex ne fonctionne-t-elle pas ? On peut difficilement en trouver une beaucoup plus simple... Ah oui, c'est vrai, il y a une règle syntaxique un peu étrange, le métacaractère . reconnaît tout caractère à l'exception du retour à la ligne, sauf en mode multiligne, c'est cela, n'est-ce pas ?

Ajoutons le modificateur multiligne m :
Code : Sélectionner tout
1
2
 
  DB<7>  print $1 if $string =~ /Une(.+)ligne/m
Ah, zut ! ce n'est pas cela. Le contraire peut-être ? Le mode ligne simple (modificateur s) ? Voyons voir :
Code : Sélectionner tout
1
2
3
4
5
 
  DB<10>  print $1 if $string =~ /Une(.+)ligne/s; 
 ligne avec 
 un retour à la 
  DB<11>
Oui, ça marche, le résultat recherché s'affiche comme il faut.

L'intérêt du débogueur en mode interactif

Certains trouveront peut-être les exemples ci-dessus simplistes, d'autres les jugeront au contraire complexes. Le point essentiel est que, quel que soit votre niveau, le débogueur Perl permet de tester bien plus efficacement des expressions dont vous n'êtes pas sûrs ou qui sont à la limite de vos connaissances actuelles que si vous deviez les mettre dans un programme que vous testerez ensuite.

L'auteur de ces lignes utilise ce mécanisme systématiquement dès qu'il n'est pas bien sûr de sa syntaxe. Le gain de temps est vraiment considérable par rapport à une situation dans laquelle on met une ligne de code dans un programme de quelques centaines de lignes et on espèce que ça va marcher, sans savoir où ça ne marche pas si c'est le cas. Et l'on progresse vraiment sur un sujet que l'on ne maîtrise encore que partiellement.

Mis à jour le 19 juillet 2014 Lolo78

Utilisons le débogueur Perl pour dérouler pas à pas le fonctionnement d'un petit programme. Nous utiliserons le script table.pl suivant :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/perl 
  
use strict; 
use warnings; 
  
my $file = "1.00 InDev 01-Jun-2013 
1.00 InTest 15-Jul-2013 
1.00 InUAT 31-Jul-2013 
1.01 InDev 01-Jul-2013 
2.00 InDev 01-Aug-2013"; 
  
my @file_content = split( /\n/, $file ); 
my @full_data; 
  
push @full_data, [split] for @file_content; 
  
for my $line_ref (@full_data) { 
        print "$_\t" for @$line_ref; 
        print "\n"; 
}

Pour lancer le programme en mode débogage, il faut utiliser l'option -d de la ligne de commande :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
 
$ perl -d table.pl 
 
Loading DB routines from perl5db.pl version 1.33 
Editor support available. 
 
Enter h or `h h' for help, or `man perldebug' for more help. 
 
main::(table.pl:6):     my $file = "1.00 InDev 01-Jun-2013 
main::(table.pl:7):     1.00 InTest 15-Jul-2013 
main::(table.pl:8):     1.00 InUAT 31-Jul-2013 
main::(table.pl:9):     1.01 InDev 01-Jul-2013 
main::(table.pl:10):    2.00 InDev 01-Aug-2013";
Le programme se compile et affiche la première instruction exécutable du programme. Cette instruction qui s'étend sur cinq lignes (6 à 10) n'a pas encore été exécutée, le débogueur affiche la ligne qu'il va exécuter si on le lui demande.

Avant de continuer, examinons brièvement les différentes instructions qu'il est possible d'exécuter sous le débogueur.

Les commandes du débogueur

Les commandes p $var et x %data, permettant d'afficher le contenu des variables $var et %data, ont déjà été vues lors d'une question précédente.

La commande h permet d'afficher l'aide du débogueur :
Code : Sélectionner tout
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
 
  DB<1> h 
List/search source lines:               Control script execution: 
  l [ln|sub]  List source code            T           Stack trace 
  - or .      List previous/current line  s [expr]    Single step [in expr] 
  v [line]    View around line            n [expr]    Next, steps over subs 
  f filename  View source in file         <CR/Enter>  Repeat last n or s 
  /pattern/ ?patt?   Search forw/backw    r           Return from subroutine 
  M           Show module versions        c [ln|sub]  Continue until position 
Debugger controls:                        L           List break/watch/actions 
  o [...]     Set debugger options        t [expr]    Toggle trace [trace expr] 
  <[<]|{[{]|>[>] [cmd] Do pre/post-prompt b [ln|event|sub] [cnd] Set breakpoint 
  ! [N|pat]   Redo a previous command     B ln|*      Delete a/all breakpoints 
  H [-num]    Display last num commands   a [ln] cmd  Do cmd before line 
  = [a val]   Define/list an alias        A ln|*      Delete a/all actions 
  h [db_cmd]  Get help on command         w expr      Add a watch expression 
  h h         Complete help page          W expr|*    Delete a/all watch exprs 
  |[|]db_cmd  Send output to pager        ![!] syscmd Run cmd in a subprocess 
  q or ^D     Quit                        R           Attempt a restart 
Data Examination:     expr     Execute perl code, also see: s,n,t expr 
  x|m expr       Evals expr in list context, dumps the result or lists methods. 
  p expr         Print expression (uses script's current package). 
  S [[!]pat]     List subroutine names [not] matching pattern 
  V [Pk [Vars]]  List Variables in Package.  Vars can be ~pattern or !pattern. 
  X [Vars]       Same as "V current_package [Vars]".  i class inheritance tree. 
  y [n [Vars]]   List lexicals in higher scope <n>.  Vars same as V. 
  e     Display thread id     E Display all thread ids. 
For more help, type h cmd_letter, or run man perldebug for all docs.
Les commandes du débogueur les plus intéressantes et les plus couramment utilisées sont les suivantes :
  • s (single step): exécute la prochaine instruction du programme en entrant dans le code de la fonction si l'instruction suivante est un appel de fonction (exécution pas à pas) ;
  • n (next): exécute la prochaine instruction du programme si l'instruction suivante est un appel de fonction, exécute l'ensemble de l'appel et se positionne sur l'instruction suivant l'appel de la fonction ;
  • r (return): continue l'exécution de la fonction courante jusqu'à la fin de la fonction ;
  • c n (continue): continue l'exécution du programme jusqu'à la ligne n du code et jusqu'à la fin si aucun numéro de ligne n'est spécifié (sauf si une autre raison ou condition quelconque, comme l'arrivée à un point d'arrêt, interrompt l'exécution) ;
  • b n (breakpoint): ajoute un point d'arrêt sur la ligne n du code ; l'exécution s'arrête sur cette ligne si elle est rencontrée ;
  • w (watch): ajoute un point de surveillance d'une variable (l'exécution sera suspendue si la variable change de valeur).


Utiliser le mode pas à pas

Revenons maintenant à notre programme pour lequel le débogueur affichait la prochaine instruction à exécuter :
Code : Sélectionner tout
1
2
3
4
5
6
 
main::(table.pl:6):     my $file = "1.00 InDev 01-Jun-2013 
main::(table.pl:7):     1.00 InTest 15-Jul-2013 
main::(table.pl:8):     1.00 InUAT 31-Jul-2013 
main::(table.pl:9):     1.01 InDev 01-Jul-2013 
main::(table.pl:10):    2.00 InDev 01-Aug-2013";
Nous pouvons constater que la variable $file n'est pas encore initialisée, mais dès que nous utilisons la commande s pour exécuter cette première instruction, la variable $file prend la valeur voulue :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
 
  DB<2> p $file 
 
  DB<3> s 
main::(table.pl:12):    my @file_content = split( /\n/, $file ); 
  DB<3> p $file 
1.00 InDev 01-Jun-2013 
1.00 InTest 15-Jul-2013 
1.00 InUAT 31-Jul-2013 
1.01 InDev 01-Jul-2013 
2.00 InDev 01-Aug-2013
Dès que la commande s est validée, la première instruction du programme est effectuée et le débogueur affiche l'instruction suivante à exécuter (my @file_content = ...). Cette fois, la commande p $file affiche le contenu de la variable (les six lignes d'initialisation).

Continuons :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
 
  DB<4> s 
main::(table.pl:13):    my @full_data; 
  DB<4> x  \@file_content 
0  ARRAY(0x6003c9d28) 
   0  '1.00 InDev 01-Jun-2013' 
   1  '1.00 InTest 15-Jul-2013' 
   2  '1.00 InUAT 31-Jul-2013' 
   3  '1.01 InDev 01-Jul-2013' 
   4  '2.00 InDev 01-Aug-2013'
Le split a bien stocké chaque ligne en tant qu'élément du tableau @file_content.

Si nous poursuivons et exécutons les lignes 13 et 15 du code (commande s deux fois) puis observons le contenu du tableau @full_data, nous obtenons ce qui suit :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 
  DB<6> x  \@full_data 
0  ARRAY(0x60025cba0) 
   0  ARRAY(0x600513fd0) 
      0  1.00 
      1  'InDev' 
      2  '01-Jun-2013' 
   1  ARRAY(0x60006f300) 
      0  1.00 
      1  'InTest' 
      2  '15-Jul-2013' 
   2  ARRAY(0x6005ebc30) 
      0  1.00 
      1  'InUAT' 
      2  '31-Jul-2013' 
   3  ARRAY(0x600501a88) 
      0  1.01 
      1  'InDev' 
      2  '01-Jul-2013' 
   4  ARRAY(0x6004a8508) 
      0  2.00 
      1  'InDev' 
      2  '01-Aug-2013'
Le tableau @full_data est bien un tableau (AoA) contenant toutes les données utiles pour la suite. Nous pouvons terminer le programme avec la commande c et voir le résultat s'afficher :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
 
  DB<7> c 
1.00    InDev   01-Jun-2013 
1.00    InTest  15-Jul-2013 
1.00    InUAT   31-Jul-2013 
1.01    InDev   01-Jul-2013 
2.00    InDev   01-Aug-2013 
Debugged program terminated.  Use q to quit or R to restart,
Le programme utilisé n'avait apparemment pas d'erreur, mais en déroulant pas-à-pas son exécution, on a pu comprendre en détail son fonctionnement et vérifier que les structures de données utilisées étaient alimentées correctement.

Mis à jour le 20 juillet 2014 Lolo78

Utiliser le débogueur pas à pas (avec les commandes s et n) est souvent très utile, mais cela peut devenir long et pénible si le programme à beaucoup de lignes de code ou s'il exécute une boucle un grand nombre de fois. Certaines commandes permettent de sauter directement à l'endroit du programme qui nous intéresse.

La commande c (continue)

Utilisée seule, la commande c lance l'exécution du programme jusqu'à la fin, sauf si une autre commande faite antérieurement (par exemple la pose d'un point d'arrêt) entraîne la suspension de son exécution.

La commande c permet aussi la pose d'un point d'arrêt unitaire (une seule fois).

  • c nom_fonction : pour arrêter l'exécution du programme juste avant le début de la fonction nom_fonction.
  • c 17 : pour arrêter l'exécution du programme juste avant l'exécution de la ligne 17 du code.


La commande w (watch)

La commande w permet de surveiller l'évolution de la valeur d'une variable. Si l'on a tapé la commande w $var et que l'on émet ensuite la commande c, le programme s'exécutera jusqu'à ce que le programme rencontre une instruction entraînant un changement de la valeur de la variable $var. Le débogueur affichera alors l'ancienne et la nouvelle valeur. Par exemple :

Code : Sélectionner tout
1
2
3
4
5
6
7
 
  DB<2> w $d 
  DB<3> c 
Watchpoint 0:   $d changed: 
    old value:  '' 
    new value:  '30' 
main::c(test.pl:16):               print $d if $d > 20;
À noter que le programme s'arrête après la ligne de code ayant entraîné la modification de la valeur de la variable.

La commande b (breakpoint)

La commande b pose un point d'arrêt permanent sur une ligne de code ou une fonction :

  • b nom_fonction : pose un point d'arrêt juste avant le début de la fonction nom_fonction ;
  • b 17 : pose un point d'arrêt juste avant l'exécution de la ligne 17 du code ;
  • b : pose un point d'arrêt juste sur la ligne courante du code.


Pour ces usages, on utilise plus souvent la commande c (point d'arrêt unitaire) que la commande b (point d'arrêt permanent).

La commande b permet aussi la pose d'un point d'arrêt conditionnel la rendant immensément utile :
  • b 17 condition : pose un point d'arrêt juste avant l'exécution de la ligne 17 du code, mais ce point d'arrêt ne sera effectif que si la condition est satisfaite.


Considérons le petit programme suivant :
Code perl : Sélectionner tout
1
2
3
4
5
6
use strict; 
use warnings; 
  
for my $i (1..10) {  
	print 2 * $i, "\n";  
}

Exécutons-le maintenant sous le débogueur en posant un point d'arrêt conditionnel sur la ligne 5 (le print ...) si la variable $i est plus grande que 7 :

Code : Sélectionner tout
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
$ perl -d test_break.pl 
 
Loading DB routines from perl5db.pl version 1.33 
Editor support available. 
 
Enter h or `h h' for help, or `man perldebug' for more help. 
 
main::(test_break.pl:4):        for my $i (1..10) { 
  DB<1> b 5 $i > 7 
  DB<2> c 
2 
4 
6 
8 
10 
12 
14 
main::(test_break.pl:5):                print 2 * $i, "\n"; 
  DB<2> p $i 
8 
  DB<3> c 
16 
main::(test_break.pl:5):                print 2 * $i, "\n"; 
  DB<3> p $i 
9 
  DB<4> c 
18 
main::(test_break.pl:5):                print 2 * $i, "\n"; 
  DB<4> c 
20 
Debugged program terminated.  Use q to quit or R to restart,
On remarque que le programme exécute la boucle sept fois, puis s'arrête sur la ligne 5 du code à chaque itération suivante puisque la variable $i atteint une valeur supérieure à 7.

Mis à jour le 3 août 2014 Lolo78

On a parfois beaucoup de difficulté à résoudre un warning :

Code : Sélectionner tout
Use of uninitialized value...
Perl indique la ligne de code concernée et éventuellement la ligne du fichier lu où cela arrive, mais cela ne suffit souvent pas, en particulier quand cet avertissement est émis pour seulement quelques éléments d'une longue liste ou quelques rares fois dans une grosse boucle while ou foreach. Même sous le débogueur, il n'est pas possible d'exécuter pas à pas une boucle contenant plusieurs milliers, voire plusieurs millions d'itérations.

Une astuce permet de demander au débogueur de s'arrêter sur l'émission d'un warning de ce type (ou autre, d'ailleurs).

Considérons le programme suivant :

Code perl : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
  
use strict; 
use warnings; 
  
my @array = 0..5; 
$array[$_] = $_ for 7..10; 
  
foreach (@array) { 
	my $count = $_ + 5; 
	print $count, "\n"; 
} 
print "end\n";

Avant d'entrer dans la boucle foreach, le tableau @array a le contenu suivant :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
  DB<2> x @array 
0  0 
1  1 
2  2 
3  3 
4  4 
5  5 
6  undef 
7  7 
8  8 
9  9 
10  10
On voit que le septième élément du tableau (indice 6) n'est pas défini.

À l'exécution, cela donne le warning suivant et une valeur probablement fausse :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ perl  test_debug_warn.pl 
5 
6 
7 
8 
9 
10 
Use of uninitialized value $_ in addition (+) at test_debug_warn.pl line 11. 
5 
12 
13 
14 
15 
end
L'utilisation de débogueur n'aide pas vraiment dans ce genre de cas :

Code : Sélectionner tout
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
$ perl -d test_debug_warn.pl 
 
Loading DB routines from perl5db.pl version 1.33 
Editor support available. 
 
Enter h or `h h' for help, or `man perldebug' for more help. 
 
main::(test_debug_warn.pl:6):   my @array = 0..5; 
  DB<1> c 
5 
6 
7 
8 
9 
10 
Use of uninitialized value $_ in addition (+) at test_debug_warn.pl line 11. 
 at test_debug_warn.pl line 11 
5 
12 
13 
14 
15 
end 
Debugged program terminated.  Use q to quit or R to restart, 
  use o inhibit_exit to avoid stopping after program termination, 
  h q, h R or h o to get additional info.
Avec une boucle de 11 itérations, on peut certes aller pas à pas jusqu'à la ligne posant problème afin d'examiner le contexte et l'état des variables au moment de l'émission du warning, mais ce n'est plus possible pour une boucle contenant un grand nombre d'itérations. C'est là que l'on voudrait demander au débogueur de s'arrêter au moment du warning.

Il suffit pour cela de configurer le débogueur pour qu'il s'arrête sur les warnings en plaçant, dans le répertoire courant ou dans le répertoire de l'utilisateur, un fichier nommé .perldb (c'est un fichier de configuration chargé lors du lancement du débogueur) et contenant le code suivant :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
sub afterinit  
{ 
    $::SIG{'__WARN__'} = sub { 
        my $warning = shift; 
        if ( $warning =~ m{uninitialized}xms ) { 
            $DB::single = 1;    # point d'arrêt, le débogueur s'arrête ici 
        } 
        warn $warning; 
    }; 
    print "sigwarn handler installed!\n"; 
    return; 
}
Sans entrer dans le détail, disons que cela ajoute un point d'arrêt à la fonction de warning du débogueur.

Maintenant, le débogueur s'arrête juste après l'émission du warning, ce qui permet d'examiner le contenu de la variable $_ à ce moment-là :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ perl -d test_debug_warn.pl 
 
Loading DB routines from perl5db.pl version 1.33 
Editor support available. 
 
Enter h or `h h' for help, or `man perldebug' for more help. 
 
sigwarn handler installed! 
main::(test_debug_warn.pl:6):   my @array = 0..5; 
  DB<1> c 
5 
6 
7 
8 
9 
10 
Use of uninitialized value $_ in addition (+) at test_debug_warn.pl line 11. 
main::(test_debug_warn.pl:12):          print $count, "\n"; 
  DB<1> x $_ 
0  undef
À noter que sous Unix, on peut avoir besoin de modifier les droits sur le fichier .perldb, mais le message émis par le débogueur est parfaitement clair.

Mis à jour le 3 août 2014 Lolo78

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

 
Responsable bénévole de la rubrique Perl : djibril -