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 !
- Comment déboguer mon programme Perl ?
- À quoi sert le pragma "use strict;" ?
- À quoi servent les warnings ?
- Je viens d'avoir ce warning que je ne comprends pas. Que veut-il dire ?
- Je sais que ma variable contient "toto", mais quand je teste si elle contient "toto", ça ne marche pas. Pourquoi ?
- Un affichage plus utile qu'un simple print
- Et pour afficher le contenu d'un tableau ou d'un hash ?
- Comment se débarrasser des messages de débogage quand on n'en a plus besoin?
- Savoir comment le compilateur Perl a compris le code qui lui a été passé
- Tester des expressions complexes ou des fonctions inhabituelles avant de les inclure dans le programme
- Utiliser le débogueur Perl pour tester le langage de façon interactive
- Utiliser le débogueur Perl pour visualiser le déroulement d'un programme
- Comment accélérer le travail sous le débogueur ?
- Comment arrêter le débogueur Perl sur un warning du genre "Use of uninitialized value ..."
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 |
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.
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
10use 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
8use 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
4use 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
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; |
Code : | Sélectionner tout |
Use of uninitialized value in concatenation (.) or string at ...
Code perl : | Sélectionner tout |
$months[$_] = 0 for 1..12;
Code perl : | Sélectionner tout |
1 2 3 | for my $mois (1..12) { print "$mois: $months[$mois] \n" if defined $months[$mois] } |
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); } |
Code : | Sélectionner tout |
1 2 | $ perl recursive_sum.pl 99 4950 |
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 |
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 |
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); } } |
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.
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 |
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 |
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 |
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 |
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 |
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 ] |
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"}' |
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 |
Code : | Sélectionner tout |
1 2 | % perl mon_programme.pl input.txt | perl autre_programme.pl |
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 |
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 |
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 |
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 |
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 |
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; |
Code : | Sélectionner tout |
1 2 3 4 5 6 | 0 : janvier 1 : fevrier 2 : mars 3 : avril 4 : mai 5 : juin |
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' ]; |
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' }; |
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' ] } } ]; |
Un autre outil intéressant pour ce genre d'affichage est le module Data::Dump qui fournit un affichage plus compact.
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; |
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"; } |
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.
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 $? |
Code : | Sélectionner tout |
1 2 3 | yes 3 |
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 |
- 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); |
Code : | Sélectionner tout |
1 2 3 4 | $ perl test.pl; echo $? yes 1 |
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 |
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! |
Code : | Sélectionner tout |
perl -e "print 'hello world!';"
Code : | Sélectionner tout |
if($str1 =~ /$str2/gi){...
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 |
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 |
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> |
À 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.
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 |
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' |
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 |
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 |
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 |
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 |
Ajoutons le modificateur multiligne m :
Code : | Sélectionner tout |
1 2 | DB<7> print $1 if $string =~ /Une(.+)ligne/m |
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> |
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.
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"; |
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. |
- 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"; |
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 |
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' |
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' |
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, |
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; |
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 a parfois beaucoup de difficulté à résoudre un warning :
Code : | Sélectionner tout |
Use of uninitialized value...
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 |
À 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 |
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. |
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; } |
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 |
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 çaLes 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 © 2024 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.