Perl pour faire des scripts efficaces

Anne langage perl script utf-8 xml heredoc

J’ai été amenée à traduire certains de mes scripts bash qui prenait vraiment trop de temps à s’exécuter : le gain en performance a été spectaculaire !

Pour faire du code propre

La commande perlcritic permet de vérifier un certain nombre de règles de bonnes pratiques sur les scripts perl. La plupart des règles viennent d’un livre Perl Best Practices qui n’est pas libre, mais on a quand même accès à la liste des règles.

On peut aussi voir la liste des règles par :

perlcritic -list

puis, pour les détails sur une règle, faire par exemple :

perlcritic --doc RequireBlockGrep

L’utilisation la plus simple est :

perlcritic script.pl

pour vérifier un fichier, et on peut aussi vérifier tout un répertoire.

Le niveau de vérification par défaut est -severity 5, et on peut diminuer ce niveau pour vérifier plus de choses.

On peut aussi écrire un fichier de configuration .perlcriticrc pour modifier le comportement par défaut (modifier le niveau de certaines règles, ignorer des règles, etc).

Pour plus d’informations, voir la documentation du module Perl::Critic.

Structures de donnée

Par contre, je pense que ceux qui ont inventé ce langage étaient un peu fou, car la syntaxe détermine le type des objets :

  • scalaire ou chaîne de caractère : $x ;
  • liste : @l :
    • liste vide : ()
    • accès au 2e élément : $x = $l[1];
    • accès au dernier élément : $x = $l[-1];
    • ajout d’un élément : push @l $x;
    • slice : @slice = @l[5..10];
    • référence : $ref_l = \@l;
    • référence sur une liste vide : $ref_l = [];
    • déréférence : @l = @{$ref_l};
    • accès au 2e élément de la référence : $x = $ref_l->[1];
  • table associative (hash) : %bdd
    • initialisation :
        %bdd = (
           chien => "ouah ouah",
           chat => "miaou"
           );
  • ajout d’un élément : $bdd{oiseau} = "cui cui";
  • référence $ref_bdd = \%bdd;
  • référence sur une table vide : $ref_bdd = {};

Voir aussi le chapitre du livre Programming Perl : Data Structures.

Pointeur de fonction

my $f = $cond ? \&fonction1 : \&fonction2;
&$f ();

fonction1 et fonction2 doivent être des fonctions définies, et f est le pointeur de fonction. Comme pour les autres références, pour y accéder, il faut le préfixer par le type d’objet, c’est-à-dire & pour une fonction.

Pour faire une mise à jour

Il faut savoir que cpan est le gestionaire de paquets de perl. Pour faire une mise à jour :

$ sudo cpan
cpan[1]> upgrade

Options simples

Quand on utilise l’option -s de perl, les options sont traitées comme des variables. Par exemple :

#! /usr/bin/perl -w -s
our $v;
my $verbose = $v || 0;

met $verbose à 0 si on n’a pas utilisé l’option -v, à 1 si on a utilisé l’option -v, ou même à 2 si on fait -v=2.

Autre exemple :

#! /usr/bin/perl -w -s
our $version;
my $version_num = "0.1";
my $version_date = "May 2013";
if ($version) {
    print "Version $version_num ($version_date)\n";
    exit (0);
  }

Lire un fichier XML

Il y a un moyen très simple de lire un fichier XML, c’est d’utiliser XML::Parser::PerlSAX. Pour cela, il faut d’abord créer un module qui dit ce qu’il faut faire. Par exemple :

#!/usr/bin/perl -w
package myXML;

use strict;
use warnings;
use XML::Parser::PerlSAX;

sub new {
  my $type = shift;
  return bless {}, $type;
}

sub start_element ($$) {
  my ($self,$elt) = @_;
  my $name = $elt->{ Name };
  my $attr = $elt->{ Attributes };

  while (my ($data, $val) = each %{$attr}) {
    printf ("$name : $data = $val\n");
  }
}

Après, il suffit de l’invoquer par :

my $file = $ARGV[0];
die "I need a file to read !" unless $file;

my $handler = myXml->new();
my $parser  = XML::Parser::PerlSAX->new( Handler => $handler );
$parser->parse( Source => { SystemId => $file } );

qui peut être mis soit dans le même fichier si on fait un petit script, soit dans un autre fichier pour un développement plus gros.

On peut bien sur utiliser d’autres fonctions dans le handler comme end_element par exemple (voir la doc pour plus d’information).

Produire un graphe .dot

Perl propose un module GraphViz assez facile à utiliser.

Création du graphe

Par exemple :

my $graph = GraphViz->new(directed => 1, rankdir => 'LR');

Ajout de noeuds et d’arrêtes

Par exemple :

$graph->add_node ($n1, label => $label1, shape => 'box');
$graph->add_node ($n2, label => $label2, shape => 'box');
$graph->add_edge ($n1, $n2, color => 'red');

Export dans un fichier

print $file $g->as_text;

Texte sur plusieurs lignes

Il est parfois pratique d'écrire un texte sur plusieurs lignes, en particulier pour les fonctions d’aides. En PERL comme ailleurs, on peut utiliser la syntaxe here-doc comme ceci :

sub usage () {
  print STDERR <<END;
Usage : ./outil.pl <options> fichier.xml
  cet outil traite fichier.xml et affiche des informations.
Options :
  -help \t affiche ce message.
  -trucs \t affiche des informations au sujet des trucs.
  -truc=<nom> \t affiche des informations au sujet du truc <nom>.
END
}

Retrouver les packages

Si on développe un script en le découpant en plusieurs modules (script principal .pl et modules (package) .pm), et qu’on lance le script depuis un autre répertoire, il ne retrouve pas ses petits… Pour y remédier, on peut utiliser

use FindBin;
use lib "$FindBin::Bin";

dans le script principal, afin d’aller chercher les modules au même endroit.

Gestion utf-8

La gestion de utf-8 par perl n’est pas trivial. Le plus simple, si on veut n’avoir que de l’utf_8 partout, c’est de commencer le script par :

#!/usr/bin/perl -CSDA
use utf8;
  • -C contrôle la prise en compte de Unicode (on peut aussi utiliser la variable d’environnement PERL_UNICODE)
    • S = IOE ;
      • I spécifie que l’on veut utiliser utf-8 pour STDIN ;
      • O spécifie que l’on veut utiliser utf-8 pour STDOUT ;
      • E spécifie que l’on veut utiliser utf-8 pour STDERR ;
    • D = io ;
      • i spécifie que l’on veut utiliser utf-8 pour tous les fichiers lus ;
      • o spécifie que l’on veut utiliser utf-8 pour tous les fichiers écrits ;
    • A spécifie que l’on veut utiliser utf-8 pour @ARGV.

Attention, si on utilise perl depuis la ligne de commande plutôt que de lancer le script directement, on a le message :

Too late for "-CSDA" option at dicopedia.pl line 1.

Il faut alors aussi utiliser -CSDA sur la ligne de commande.

Voir aussi :