Article publié dans Linux Magazine 88, novembre 2006.
La perle de ce mois-ci a été rédigée par Philippe "BooK" Bruhat
(book@mongueurs.net
), de Lyon.pm et Paris.pm.
Le site http://search.cpan.org/ fournit une interface particulièrement agréable vers CPAN.
Prenons un exemple précis : si un module a été maintenu par plusieurs auteurs au fil du temps, les différentes distributions seront disséminées dans les répertoires de chacun de ceux qui les auront déposées sur CPAN. Sur Search CPAN, un URL de la forme http://search.cpan.org/dist/Maypole/ (distribution dont plusieurs personnes se sont occupé au fil du temps) pointera vers la version la plus récente, mais la page obtenue pointera également vers les pages de toutes les autres versions, quel qu'en soit l'auteur.
Si vous disposez déjà d'un mini CPAN (voir les Perles 8 et 9, parues il y déjà quelque temps), vous avez déjà chez vous les toutes dernières versions de chaque distribution. Le script qui suit va vous permettre de récupérer les versions plus anciennes qui n'ont pas encore été supprimées du CPAN.
Si NOM
est le nom de la distribution recherchée, alors la page qui
la concerne sur Search CPAN est http://search.cpan.org/dist/NOM.
Cette page est générée automatiquement par un programme, nous pouvons
donc être sûrs que le format sera identique pour toutes les pages,
quelle que soit la valeur de NOM
>.
En prenant par exemple Maypole
, la page en question contient :
un lien Download qui pointe vers la version la plus à jour de la distribution,
<tr> <td class=label>This Release</td> <td class=cell>Maypole-2.11_pre1</td> <td><small> [<a href="/CPAN/authors/id/T/TE/TEEJAY/Maypole-2.11_pre1.tar.gz">Download</a>] [<a href="/src/TEEJAY/Maypole-2.11_pre1/">Browse</a>] </small></td> <td><small>14 Apr 2006</small> </td> </tr>
et un formulaire avec une liste de toutes les autres versions disponibles de la distribution.
<tr> <td class=label>Other Releases</td> <td class=cell colspan=3> <form action="/redirect"> <select name="url"> <option value="/~teejay/Maypole-2.10/">Maypole-2.10 -- 19 Jul 2005</option> <option value="/~simonflk/Maypole-2.09/">Maypole-2.09 -- 25 Jan 2005</option> <option value="/~simonflk/Maypole-2.08/">Maypole-2.08 -- 24 Jan 2005</option> ... </form></td></tr>
Il n'y a pas besoin de beaucoup expérimenter pour trouver que l'expression
régulière m!href="/(CPAN/.*?)"!g
capturera le lien de téléchargement,
et m!<option value="/(.*?)"!g
les liens vers les pages des autres
versions de la distribution sur Search CPAN.
En testant avec la distribution perl
, j'ai pu constater la présence
de la distribution perl-5.6-info
qui n'est pas une distribution Perl,
mais la conversion de la documentation de Perl 5.6 au format TeXinfo.
Afin d'éviter les téléchargements inutiles (et des problèmes avec
grep, voir plus loin), j'ai donc ajouté une option de filtrage qui
prend en paramètre une expression régulière et qui ne téléchargera pas
les URL correspondantes ni n'imprimera les liens vers les archives
associées.
#!/usr/bin/perl use strict; use warnings; use LWP::Simple; use List::Util qw( first ); use Getopt::Long; # option de ligne de commande my %conf = ( filter => '^$' ); GetOptions( \%conf, 'filter=s' ) or die 'Usage: all_dist [--filter re] dist ...'; my $base = 'http://search.cpan.org'; $|++; # vidage automatique du tampon de sortie my %tgz; for my $name (@ARGV) { # le point de départ de la distribution $tgz{"dist/$name"} ||= ''; while ( my $dist = first { !$tgz{$_} && !/$conf{filter}/o } keys %tgz ) { # récupère le contenu de la page my $content = get("$base/$dist"); # le lien de téléchargement (avec filtrage) print grep { !/$conf{filter}/o } map {"$base/$_\n"} ( $tgz{$dist} ) = $content =~ m!href="/(CPAN/.*?)"!g; # les "autres" liens $tgz{$_} ||= '' for $content =~ m!<option value="/(.*?)"!g; } }
Le script est finalement assez simple : la table de hachage %tgz
a pour clés les URL des pages sur search.cpan.org
et pour valeurs
les fichiers pointés par le lien Download de chaque page. La condition
de la boucle while()
récupère la première clé qui ne correspond pas
à l'expression de filtrage (stockée dans $conf{filter}
) et dont la
valeur est la chaîne vide, c'est-à-dire les pages dont on n'a pas encore
récupéré l'URL du lien Download.
Au début de chaque tour de boucle sur @ARGV
, on initilialise donc
%tgz
avec $tgz{"dist/$name"} ||= ''
(c'est-à-dire qu'on s'assure
que la clé existe en lui mettant une valeur vide, sauf si une valeur
vraie existe déjà).
get("$base/$dist")
récupère le contenu de la page avec LWP::Simple
,
sur lequel on peut ensuite utiliser nos deux expressions régulières.
La première est utilisée pour extraire le lien de téléchargement, la seconde
(l'expression de filtrage stockée dans $conf{filter}
) permet de
vérifier s'il lui correspond afin de ne pas l'afficher.
Notez l'utilisation de m//g
en contexte de liste pour récupérer le
résultat de la capture. La première fois, le contexte est forcé avec les
parenthèses dans ( $tgz{$dist} ) = $content =~ m!href="/(CPAN/.*?)"!g
),
puis c'est ensuite le for
qui impose un contexte de liste.
Une petite remarque sur un détail qui m'a causé pas mal de problèmes
lors de la création de ce script : $base
ne contient pas
de /
et les expressions régulières ne capturent pas le premier
/
. D'une part, cela améliore la lisibilité de "$base/$dist"
),
et d'autre part un double slash (//
) dans l'URL va poser problème à
Search CPAN. En effet, si pour lui http://search.cpan.org/dist/Maypole
et http://search.cpan.org//dist/Maypole sont identiques, en
revanche http://search.cpan.org//~simonflk/Maypole-2.08/ (notez le
//
) provoque une erreur 500 (et le rigolo message suivant du serveur :
Whoops, it seems that I choked while trying to process your request.
Please forgive me its (sic) the cook here, he is still rather new.).
J'ai voulu tester l'installation de toutes les versions de Perl disponibles, et j'ai donc utilisé ce script de la façon suivante :
$ all_dist perl | grep --line-buffered -v info | xargs -n 1 wget -c
Pour télécharger les fichiers sans avoir à attendre que Perl ait fini
de tout récupérer, il faut forcer chaque élément
de la chaîne à afficher ou à lire les lignes disponibles dès que
possible. On passe donc en mode de tampon de ligne (line-buffered),
pour Perl avec $|++
, pour grep avec l'option --line-buffered
et en forçant xargs à passer les paramètres un par un à wget.
C'est pour éviter de passer par ce grep --line-buffered
que j'ai
ajouté l'option --filter. La commande devient donc :
$ all_dist --filter info perl | xargs -n 1 wget -c
Pour lancer plusieurs wget en parallèle, on peut utiliser l'option -P de xargs, qui permet de définir le nombre de processus lancés en parallèle (par défaut 1, 0 signifiant « pas de limite »). On peut également jouer avec l'option -n pour passer plusieurs URL à une même instance de wget.
Bien sûr, on peut également sauver toutes ces URL dans un fichier
perl.lst, faire un peu de tri manuellement et appeler wget -i perl.lst
.
Notez bien que les URL renvoyées par Search CPAN redirigeront vos
requêtes vers les divers miroirs CPAN existant dans votre région (j'ai
ainsi eu l'occasion de passer par à peu près tous les miroirs français de
CPAN quand j'ai récupéré toutes les distributions de Perl disponibles).
Le fichier en question donne les liens vers 37 versions de Perl, chez 12 auteurs différents.
Créez votre mini-CPAN, par Sébastien Aperghis-Tramoni, paru dans GNU/Linux Magazine France N°67, décembre 2004.
http://articles.mongueurs.net/magazines/perles/perles-08.html#h1
Intégrer des modules non-publics dans un mini-CPAN, par Sébastien Aperghis-Tramoni, paru dans GNU/Linux Magazine France N°68, janvier 2005.
http://articles.mongueurs.net/magazines/perles/perles-09.html#h2
Envoyez vos perles à perles@mongueurs.net
, elles seront peut-être
publiées dans un prochain numéro de Linux Magazine.
Copyright © Les Mongueurs de Perl, 2001-2011
pour le site.
Les auteurs conservent le copyright de leurs articles.