• Aucun résultat trouvé

Petits scripts en Perl et Bash pour manipuler ses fichiers Pierre-Louis Cayrel, Th´eophane Lumineau 30 mars 2009

N/A
N/A
Protected

Academic year: 2021

Partager "Petits scripts en Perl et Bash pour manipuler ses fichiers Pierre-Louis Cayrel, Th´eophane Lumineau 30 mars 2009"

Copied!
33
0
0

Texte intégral

(1)

Petits scripts en Perl et Bash pour manipuler ses fichiers

Pierre-Louis Cayrel, Th´ eophane Lumineau 30 mars 2009

Cet article propose quelques petits trucs en Perl et en Bash pour manipuler des fichiers.

Mon id´ee est la suivante, partir des scripts fait par les mongueurs et autres amateurs de Perl et Bash, les regrouper dans un fichier, les commenter de mani`ere claire et sans blabla inutile, ´etayer avec d’autres sources (commande shell et script). Ceci dans le but d’avoir LE pdf sur la gestion des fichiers avec les scripts qui vont bien.

Je vais continuer mes recherches sur le net d’uniligne pour la gestion des fichiers, cr´eer les miens r´epon- dant `a certaines probl´ematiques et j’enrichirai ce fichier au fur et `a mesure.

Ce fichier a pour sens d’´evoluer, tous les lecteurs peuvent m’´ecrire pour apporter leur contributions.

Mots clefs :Perl, Bash, manipulation de fichiers.

(2)

Table des mati` eres

I Fichiers 5

1 Comment convertir tous ces fichiers .toto en .tata ? 5

2 Copie de fichiers 5

3 Ajouter un pr´efixe aux fichiers trait´es 5

4 Sauvegarder les originaux dans un r´epertoire 5

5 Supprime les fichiers temporaires d’emacs 5

6 Compte les paragraphes d’un fichier 5

7 Imprime les lignes communes aux deux fichiers 5

8 Imprime les lignes communes `a 3 fichiers 5

9 D´etecte les fichiers texte 6

10 Modifie des dates d’acc`es et de modification du fichier, pour affirmer qu’ils datent d’un

mois dans le futur. 6

11 Ajoute un COMMIT toutes les 500 lignes d’un gros fichier SQL d’insertion 6

12 D´ecode et imprime un fichier encod´e en base64 6

13 dos2unix 6

14 mac2unix 6

15 Convertit tous les noms de fichiers du r´epertoire courant en minuscules, et meurt en cas

de probl`eme 6

16 Effaceur de fichiers temporaires 6

17 D´ecouper un fichier en blocs de n lignes 6

18 D´ecouper un fichier en blocs de n lignes suite 7

19 S´electionner une tranche d’un fichier texte 7

20 S´electionner une tranche d’un fichier texte suite 7

21 Classer ses fichiers par date 8

22 Remplacer une ligne par une autre (les deux pass´ees en param`etre) dans un fichier : 9

II Contenu 11

23 Comment supprimer les doublons dans un fichier ? 11

(3)

24 Comment convertir un a en un b en ligne de commande dans toto.c ? 11

25 Tris en Perl 11

25.1 Trier num´eriquement une liste. . . 11

25.2 La fonction sort. . . 11

25.3 La fonction sort 2. . . 11

25.4 Tri avec r´ef´erence . . . 11

25.5 Tris multiples . . . 12

25.6 Plus petit et le plus grand des ´el´ements d’une liste : . . . 12

25.7 Transformer deux mots . . . 12

26 Remplace ”machin” par ”bidule” 12

27 Supprime les lignes en doublon 12

28 Calcule la somme du premier et dernier champ de chaque ligne : 12

29 Extrait, trie et imprime les mots d’un fichier 12

30 Affiche les lignes du fichier fichier (ou du flux re¸cu sur l’entr´ee standard) par ordre

croissant d’occurrence 12

31 Pour convertir de ISO-Latin-1 vers UTF-8 13

32 Pour convertir de UTF-16 vers ISO-Latin-1 13

33 Mini-traducteur 13

34 Affiche le premier paragraphe de la section Author de perl 13

35 mgrep 14

36 Supprimer des doublons 14

37 Supprimer les doublons 2 15

38 Calculer un handle de fichier 15

39 La fonction reduce() 16

40 Minimum et maximum d’une liste 17

41 Compter le nombre de lignes dans une chaˆıne 18

42 La fonction pos() 18

43 D´ecoupage en tranches 19

III Annexes 19

44 Extrait l’en-tˆete d’un mail 19

45 Extrait le corps d’un mail : 20

(4)

46 Supprime la plupart des commentaires d’un source C 20

47 Trouve le premier UID non utilis´e 20

48 Num´erote les lignes d’un fichier 20

49 Conversion de secondes 20

50 Retrouvez votre adresse IP 20

51 Tester un compte POP 20

52 G´en´erer toutes les adresses IP de plusieurs sous-r´eseaux 21

53 G´en´erer une liste de nombres 21

54 Valeurs hexad´ecimales des nombres de 27 `a 33 21

55 G´en´erer une bˆete liste de nombres 21

56 L’idiome substr() = ”toto” 22

57 Visualisation de la progression 22

58 Les parenth`eses ne font pas les listes 22

59 D´ecouper un fichier diff (une rustine, quoi) 23

60 R´ecup´erer ses mails 24

61 Un (autre) robot de traduction 26

62 Mesurer son d´ebit avec l’aide de Free 27

63 Fractionner une image 30

64 D´ecouper des MP3 avec Perl 31

(5)

Premi` ere partie

Fichiers

1 Comment convertir tous ces fichiers .toto en .tata ? 2 Copie de fichiers

#!/bin/bash

# "bkup" - copie les fichiers sp´ecifi´es dans le r´epertoire ~/Backup

# de l’utilisateur apr`es avoir v´erifi´e qu’il n’y a pas de conflits de nom.

a=$(date +’%Y%m%d%H%M%S’) cp -i $1 ~/Backup/$1.$a chmod +x bkup

# pour l’ex´ecuter, saisissez simplement ./bkup fichier.txt

3 Ajouter un pr´ efixe aux fichiers trait´ es

$ perl -i ’orig_*’ -pe ’s/\bfoo\b/toto/g;s/\bbar\b/titi/g’ fichier1 fichier2

4 Sauvegarder les originaux dans un r´ epertoire

$ perl -i ’orig/*.bak’ -pe ’s/\bfoo\b/toto/g;s/\bbar\b/titi/g’ fichier1 fichier2

5 Supprime les fichiers temporaires d’emacs

$ find $HOME -name ’*~’ -print0 | perl -n0e unlink

6 Compte les paragraphes d’un fichier

$ perl -n000e ’END{print "$. paragraphes\n"}’ fich

7 Imprime les lignes communes aux deux fichiers

perl -ne ’print if ($seen{$_} .= @ARGV) =~ /10$/’ fichier1 fichier2

8 Imprime les lignes communes ` a 3 fichiers

perl -ne ’print if ($seen{$_} .= @ARGV) =~ /21+0$/’ fichier1 fichier2 fichier3

(6)

9 D´ etecte les fichiers texte

perl -le ’for(@ARGV) {print if -f && -T _}’ *

10 Modifie des dates d’acc` es et de modification du fichier, pour affirmer qu’ils datent d’un mois dans le futur.

perl -e ’$X=24*60*60; utime(time(),time() + 30 * $X,@ARGV)’ fichier

11 Ajoute un COMMIT toutes les 500 lignes d’un gros fichier SQL d’insertion

perl -ple ’print "COMMIT;" unless $. % 500’ fichier.sql

12 D´ ecode et imprime un fichier encod´ e en base64

(tel que fourni par uuencode -m, par exemple)

perl -MMIME::Base64 -pe ’$_ = decode_base64($_)’ fichier_base64

13 dos2unix

perl -pi -e ’s/\r\n/\n/g’ fichier_dos.txt

14 mac2unix

perl -w015l12pi.bak fichier_mac.txt

15 Convertit tous les noms de fichiers du r´ epertoire courant en minuscules, et meurt en cas de probl` eme

perl -e ’rename $_, lc or die $! for <*>’

16 Effaceur de fichiers temporaires

find $HOME -name ’*~’ -print0 | perl -n0e unlink

17 D´ ecouper un fichier en blocs de n lignes

R´ecemment, un coll`egue a eu besoin de d´ecouper un gros fichier en blocs de 65534 lignes (car Excel tronque les fichiers texte CSV qu’il importe `a 65535, c’est embˆetant).

#!perl -wn

BEGIN { $file = "partie00"; }

if( $. % 65534 == 1) { # NOTE: $. commence `a 1 close F; # ferme le fichier pr´ec´edent

(7)

open F, "> $file.csv"

or die "Impossible de cr´eer $file.csv: $!";

$file++; # auto-incr´ement magique }

print F;

18 D´ ecouper un fichier en blocs de n lignes suite

Voici le script pr´ec´edent modifi´e pour d´ecouper un gros fichier en morceaux tenant sur une disquette :

#!perl -wn BEGIN {

$file = "partie00";

$/ = \1024; # lecture par blocs de 1 Ko

$n = 0;

}

unless( $n++ % 1440 ) { # une disquette contient 1440 Ko close F;

open F, "> $file.csv"

or die "Impossible de cr´eer $file.csv: $!";

$file++;

}

print F;

19 S´ electionner une tranche d’un fichier texte

D´ecouper un fichier texte en morceaux, c’est bien, mais il y a des fois o`u on voudrait pouvoir simplement ne retenir qu’une partie du fichier, ne conserver qu’un bloc contenu entre certaines lignes. Il peut y avoir moyen de bricoler avec des outils comme tail(1) et head(1), mais pourquoi perdre du temps `a s’escaguasser avec ¸ca quand il est si facile de le faire en Perl.

$ perl -ne ’18..21 and print’ long_texte.txt

20 S´ electionner une tranche d’un fichier texte suite

Dans ce cas-ci, il n’affichera que les lignes 18 `a 21 du fichier long_texte.txt. Toutefois il serait plus pratique d’en faire un script auquel on pourrait passer les lignes `a afficher en param`etres. ´Ecrivons donc ce script, que nous nommons splice pour faire r´ef´erence `a la fonction du mˆeme nom en Perl, mais qui travaille elle sur les tableaux.

#!/usr/bin/perl

my($first,$last) = (shift,shift);

$.==$first .. $.==$last and print while <>

Si on invoque ce script ainsi :

$ splice 185 202 long_texte.txt

il affichera les lignes 15 `a 20 (incluses) du fichier long_texte.txt.

(8)

C’est pas mal, mais on peut faire mieux. Bien mieux. Si on change la mani`ere d’indiquer les lignes `a afficher, et qu’on adopte une syntaxe similaire `a celle de cut(1), on peut alors indiquer plusieurs blocs de lignes.

#!/usr/bin/perl

sub usage { print STDERR "usage: splice LINES [file ...]\n" and exit -1 } my $lines = shift || usage();

my(@first,@last,$i) = ();

for my $block (split ’,’, $lines) { my @l = split ’-’, $block;

push @first, $l[0];

push @last, $l[1] || $first[-1];

}

($.==$first[$i]||($.==$first[$i+1]&&++$i)) .. $.==$last[$i] and print while <>

L’exemple pr´ec´edent s’´ecrit maintenant :

$ man perl | splice 319-322

NOTES The Perl motto is ”There’s more than one way to do it.” Divining how many more is left as an exercise to the reader.

Plus int´eressant, on peut maintenant indiquer plusieurs blocs de lignes `a afficher. Pour illustrer cela, on cr´ee d’abord un fichier qui ne contient que ses num´eros de lignes :

$ pseq 1 20 "line %d" >text

ou, pour ceux qui n’auraient pas conserv´e la Perle correspondante :

$ perl -le ’print"line $_"for 1..20’ >text

Ex´ecutons maintenant splice en s´electionnant les lignes 8 `a 9, 12 et 15 `a 17.

$ splice 8-9,12,15-17 text line 8

line 9 line 12 line 15 line 16 line 17

Comme on le voit, seules les lignes indiqu´ees sont affich´ees. Quant `a ceux qui voudraient maintenant s´elec- tionner des tranches non plus en fonction des num´eros de lignes, mais en fonction du texte (en quelque sorte un m´elange des fonctionnalit´es de splice et de grep(1)), il y a moyen de faire quelque chose, mais c’est plus d´elicat de trouver une mani`ere g´en´erique de l’exprimer.

21 Classer ses fichiers par date

Si vous avez un r´epertoire mal rang´e, une premi`ere approche de sa r´eorganisation peut ˆetre de classer les fichiers par date, dans des r´epertoires judicieusement nomm´es.

$ ls -l

-rw-rw-r-- 1 book book 123 2005-05-14 17:21 bang_eth -rw-rw-r-- 1 book book 32 2005-05-14 16:54 clash

(9)

-rw-rw-r-- 1 book book 1023 2005-05-12 10:07 clunk -rw-rw-r-- 1 book book 957 2005-05-19 11:18 crraack -rw-rw-r-- 1 book book 342 2005-05-19 15:15 kayo -rw-rw-r-- 1 book book 764 2005-05-12 10:07 pam -rw-rw-r-- 1 book book 8764 2005-05-19 15:10 powie -rw-rw-r-- 1 book book 723 2005-05-13 15:41 touche -rw-rw-r-- 1 book book 1760 2005-05-18 21:32 uggh -rw-rw-r-- 1 book book 3076 2005-05-19 15:15 zlonk

L’uniligne suivant va faire l’op´eration pour nous :

$ perl -MPOSIX=strftime -MFile::Path -e ’for(glob"*"){mkpath$d=strftime"%Y-%m-%d", localtime((stat)[9]);rename$_,"$d/$_"}’

La fonction strftime() du module POSIX permet d’afficher une date en fonction d’un patron. mkpath() fournie par File : :Path permet la cr´eation des r´epertoires.

Nous obtenons le r´esultat attendu :

$ tree .

|-- 2005-05-12

| |-- clunk

| ‘-- pam

|-- 2005-05-13

| ‘-- touche

|-- 2005-05-14

| |-- bang_eth

| ‘-- clash

|-- 2005-05-18

| ‘-- uggh

‘-- 2005-05-19

|-- crraack

|-- kayo

|-- powie

‘-- zlonk

5 directories, 10 files

Sachant que mkpath() se comporte comme mkdir -p (en cr´eant les r´epertoires interm´ediaires si n´e- cessaire), on peut mˆeme imaginer des patrons avec plusieurs niveaux de profondeur, comme %Y/%m/%dou

%Y/%U (%U, %Vet%Wsont trois mani`eres de compter les semaines dans l’ann´ee).

Attention, rename(), tout comme son ´equivalent C(rename(2)) se contente de renommer le fichier ; il ne saura pas le d´eplacer physiquement d’un syst`eme de fichier `a un autre si besoin est. Pour faire des copies d’un syst`eme de fichier `a un autre, il faut utiliserFile::Copy,qui fournit des fonctionsmove()et copy() qui fonctionnent comme les commandesmvetcpusuelles. (Mais ceci d´epasse le cadre de cet uniligne.)

22 Remplacer une ligne par une autre (les deux pass´ ees en para- m` etre) dans un fichier :

#!/bin/sh

if [ $# -ne 2 ] # si le nombre de param`etres n’est pas 2

(10)

then # affiche l’usage echo Usage: $0 ligne1 ligne2 1>&2

echo Ce programme lit l’entr´ee standard, remplace ligne1 par 1>&2 echo ligne2 et ´ecrit le r´esultat sur la sortie standard 1>&2 exit 1

fi

IFS="\n" # la variable IFS (Internal Field Separator) est "ENTER"

# (utilis´ee par la commande read)

while read ligne # met dans la variable ligne une ligne de l’entr´ee standard

# tant qu’on n’est pas arriv´e a la fin do

if [ $ligne = $1 ] # si ligne est ´egale au premier param`etre then

echo $2 # affiche le deuxi`eme param`etre `a sa place else

echo $ligne # sinon, il affiche la ligne fi

done

exit 0 # sortie normale (code de retour 0)

Pour l’utiliser, ex´ecutez (par exemple) : script un deux

<fichierEntr´ee>fichierSortie.mk

(11)

Deuxi` eme partie

Contenu

23 Comment supprimer les doublons dans un fichier ?

24 Comment convertir un a en un b en ligne de commande dans toto.c ?

[A revoir]

sed ’’s|a|b|g’’ toto.c;

25 Tris en Perl

@lettres = qw( a z e r t y );

@out = sort @lettres;

# @out vaut maintenant (a,e,r,t,y,z)

25.1 Trier num´ eriquement une liste

sub par_num { return $a <=> $b }

@out = sort par_num @in

25.2 La fonction sort

La fonction sort accepte aussi directement un bloc anonyme `a la place du nom de la fonction, qui jouera le rˆole de la fonction de comparaison :

@out = sort { $b <=> $a } @in;

# ici, on trie en ordre num´erique invers´e

# (remarquez l’ordre de $a et $b)

Le bloc anonyme ´etant totalement arbitraire, nous pouvons donc r´ealiser n’importe quel tri tr`es facilement grˆace `a ce mod`ele. Voici par exemple un tri sur la date de modification des fichiers :

@out = sort { -M $a <=> -M $b } @fichiers;

25.3 La fonction sort 2

@out = sort @in; # tri lexicographique

@out = sort { $a cmp $b } @in; # m^eme chose, explicite

25.4 Tri avec r´ ef´ erence

Comme la liste `a trier peut contenir n’importe quelles donn´ees, y compris des r´ef´erences, rien ne nous empˆeche d’interpr´eter les valeurs comme nous le voulons :

@out = sort { $a->[0] <=> $b->[0] } @in

(12)

25.5 Tris multiples

out = sort { $a =~ y/e// <=> $b =~ y/e// ||

$a cmp $b } @mots

25.6 Plus petit et le plus grand des ´ el´ ements d’une liste :

my ($min, $max) = (sort @tab)[0, -1 ou

my ($min,$max) = ($tab[0]) x 2; # init. sinon warnings foreach ( @tab ) {

$min = $_ if $_ < $min;

$max = $_ if $_ > $max;

}

25.7 Transformer deux mots

Supposons que vous traduisez un document en anglais, et que vous voulez transformer tous les foo en toto et tous les bar en titi dans les exemples. Une fois que vous avez la nouvelle version, l’ancienne n’a plus d’int´erˆet pour vous.

$ perl -i -pe ’s/\bfoo\b/toto/g;s/\bbar\b/titi/g’ monfichier

26 Remplace ”machin” par ”bidule”

perl -pe ’s/\bmachin\b/bidule/g’ fichier

27 Supprime les lignes en doublon

perl -ne ’print unless $doublon{$_}++’ fichier

28 Calcule la somme du premier et dernier champ de chaque ligne :

perl -lane ’print $F[0] + $F[-1]’ fichier

29 Extrait, trie et imprime les mots d’un fichier

perl -0nal012e ’@a{@F}++; print for sort keys %a’

30 Affiche les lignes du fichier fichier (ou du flux re¸ cu sur l’entr´ ee standard) par ordre croissant d’occurrence

perl -ne ’$c{$_}++;END{print sort { $c{$a}<=>$c{$b} } keys%c}’ fichier

(13)

31 Pour convertir de ISO-Latin-1 vers UTF-8

perl -MUnicode::String=latin1 -ne ’print latin1($_)->utf8’ fichier.txt > nouveau.txt

32 Pour convertir de UTF-16 vers ISO-Latin-1

$ perl -MUnicode::String=utf16 -ne ’print utf16($_)->latin1’ fichier.txt > nouveau.txt

33 Mini-traducteur

#!/usr/bin/perl -w use strict;

use WWW::Babelfish;

my $fish = new WWW::Babelfish( agent => ’Translate/0.1’ );

die ("Babelfish indisponible\n") unless defined($fish);

my $prompt = "\n? ";

print $prompt;

while (<>) {

print $fish->translate(

source => ’English’, destination => ’French’, text => $_,

#delimiter => "\n\n", ),

$prompt;

}

34 Affiche le premier paragraphe de la section Author de perl

$ man perl | col -b | perl -ne ’/AUTHOR/../^$/ and print’

AUTHOR

Larry Wall <larry@wall.org>, with the help of oodles of other folks.

En suivant la mˆeme route que pour splice, il est simple de transformer cet uniligne en petit script mgrep (comme multi-grep :

#!/usr/bin/perl

my($first,$last) = (shift,shift);

/$first/../$last/ and print while <>

L’exemple pr´ec´edent s’´ecrit alors :

$ man perl | col -b | sgrep ’AUTHOR’ ’^$’

AUTHOR

Larry Wall <larry@wall.org>, with the help of oodles of other folks.

(14)

35 mgrep

L’´etape suivante, accepter plusieurs expressions r´eguli`eres, est celle qu’il est plus difficile de rendre aussi

´

el´egante que pour splice. En effet, dans l’id´eal nous voudrions pouvoir accepter n’importe quelle expression r´eguli`ere, mais certains caract`eres sont n´ecessaires pour la syntaxe de d´elimitation de ces expressions `a passer en argument `a mgrep (en reprenant celle de splice, on utilise le tiret pour d´elimiter les expressions d’un couple et la virgule pour d´elimiter les couples). Ces caract`eres ne pourront donc pas ˆetre utilis´es au sein des expressions r´eguli`eres, `a moins de vouloir coder un m´ecanisme d’´echappement. Nous nous en tenons `a la syntaxe de splice, en connaissant et acceptant ses limitations.

#!/usr/bin/perl use strict;

sub usage { print STDERR "usage: mgrep PATTERNS [file ...]\n" and exit -1 } my $patterns = shift || usage();

my(@first,@last,$i) = ();

for my $block (split ’,’, $patterns) { my @l = split ’-’, $block;

push @first, $l[0];

push @last, $l[1] || $first[-1];

}

(/$first[$i]/||(/$first[$i+1]/&&++$i)) .. /$last[$i]/ and print while <>

Un exemple d’ex´ecution de mgrep ressemblera `a ceci :

$ man perl | col -b | mgrep AUTHOR-’^$’,motto,virtues-why AUTHOR

Larry Wall <larry@wall.org>, with the help of oodles of other folks.

The Perl motto is "There’s more than one way to do it."

The three principal virtues of a programmer are Laziness, Impatience, and Hubris. See the Camel Book for why.

Les arguments signifient : afficher la ligne qui contient AUTHOR et le paragraphe qui suit (param`etre AUTHOR-’^$’), afficher la ligne qui contient motto (param`etre motto), afficher le texte de la ligne qui contient virtues `a la ligne qui contient why (param`etre virtues-why).

36 Supprimer des doublons

Le webmestre de http ://www.fatrazie.com/ poss`ede un fichier avec pr`es de 50 000 noms de villes fran-

¸

caises avec leurs coordonn´ees g´eographiques et leurs codes postaux. Ce fichier a ´et´e lui-mˆeme assembl´e laborieusement `a partir de diverses sources et `a l’aide de programmes Perl (dont le module WWW : :Gazet- teer : :HeavensAbove).

Le fichier courant contient une ville par ligne, avec dans l’ordre les champs nom, latitude, longitude,

´

el´evation et code postal, s´epar´es par des tabulations. En voici un extrait : Montluel 45.850 5.050 195 01120

Ni`evroz 45.833 5.067 185 01120 Pizay 45.883 5.083 284 01120 Pizay 45.733 4.333 492 01120 Thil 45.817 5.017 182 01120

Sainte-Croix 44.767 5.283 425 01120 Sainte-Croix 45.900 5.050 280 01120

(15)

Sainte-Croix 44.767 5.283 425 01120 La L´echere 45.200 6.467 1075 01121 La L´ech`ere 45.867 5.100 238 01121 La L´ech`ere 45.867 5.100 238 01121 L´ech`ere 45.583 6.333 1393 01121 Belleydoux 46.250 5.767 754 01130 Charix 46.183 5.683 758 01130

Pour nettoyer son fichier, il souhaite maintenant supprimer les doublons de villes ayant le mˆeme nom et le mˆeme code postal (les coordonn´ees g´eographiques sont souvent tr`es proches, voire identiques).

L’objectif de cette perle n’est pas seulement de vous montrer l’uniligne qui a fait tout le travail, mais surtout de vous apprendre le r´eflexe presque pavlovien de tout perleur accompli : quand vous entendez le mot unique , vous devez imm´ediatement penser table de hachage . Ensuite, tout le probl`eme est de construire la bonne cl´e pour ce hachage.

Dans le cas qui nous occupe, c’est tout simple : on consid`ere que deux villes sont identiques si elles ont le mˆeme nom et le mˆeme code postal. Notre cl´e sera donc la simple concat´enation de ces deux champs.

$ perl -lnaF\\t -e ’print unless $c{$F[0].$F[-1]}++’ FranceA-Z.txt > FranceA-unique.txt

37 Supprimer les doublons 2

Attention quand vous utilisez des cl´es composites : contrairement au cas ci-dessus, il est en g´en´eral pr´ef´erable d’utiliser un s´eparateur sp´ecifique entre ces cl´es. Cela permet d’´eviter des collisions fˆacheuses, par exemple avec des cas o`u une cl´e serait la concat´enation de ab, a et l’autre celle de a et ba.

Le probl`eme ne se posait pas dans notre cas, car il n’existe pas de ville dont le nom se termine par un nombre dans notre fichier.

Pour nous simplifier la vie, nous allons utiliser une technique remontant `a Perl 4 : l’´emulation de tableaux multi-dimensionnels (`a l’´epoque, les r´ef´erences n’existaient pas et c’´etait la seule mani`ere de faire des tableaux multi-dimensionnels). Cela consiste `a s´eparer les diff´erents ´el´ements de la cl´e par des virgules.

Notre uniligne deviendrait (on a chang´e le . en ,) :

$ perl -lnaF\\t -e ’print unless $c{$F[0],$F[-1]}++’ FranceA-Z.txt > FranceA-unique.txt Perl remplace alors $c{$F[0],$F[-1]} par $c{join $;, $F[0], $F[-1]},

comme expliqu´e dans perlvar(1) `a la section parlant de la variable

$;. Par d´efaut, $; est le caract`ere \034, qui a tout de m^eme peu de chances de se retrouver dans vos donn´ees.

38 Calculer un handle de fichier

J’ai r´ecemment dˆu faire le tri entre les bonnes lignes et les mauvaises lignes d’un fichier. Le fichier en question ´etait la sortie de comm(1). Il s’agissait de v´erifier que toutes les lignes d’un fichier A ´etaient pr´esentes dans le fichier B (A et B ´etant tri´es).

On utilise donccomm -2 A Bpour obtenir les lignes de A absentes de B et les lignes de A pr´esentes dans B. Ces derni`eres sont pr´ec´ed´ees d’une tabulation puisque comm(1) pr´esente les r´esultats en colonnes.

Pour distribuer les lignes dans les fichierA_oket A_err,on utilise l’uniligne suivant : comm -2 A B | perl -nle ’print{s/^\t//?STDOUT:STDERR}$_’ > A_ok 2> A_err

Explication : on utilise l’op´erateur ternaire ? : pour choisir vers quel filehandle ´ecrire la ligne courante : la sortie standard ou la sortie d’erreur. Le choix est conditionn´e par la pr´esence d’une tabulation en d´ebut

(16)

de ligne, que l’on enl`eve au passage (s/^\t//). Le filehandle donn´e `a print doit ˆetre soit un mot simple (bareword), soit une variable scalaire (sinon l’analyseur syntaxique de Perl n’arrive pas `a s’y retrouver).

Toute chose plus compliqu´ee que cela (comme un ´el´ement de tableau ou une expression) doit ˆetre plac´ee entre accolades :

print { expression qui renvoie un filehandle } ...

Ensuite, on utilise le shell pour rediriger la sortie standard et la sortie d’erreur vers deux fichiers diff´erents.

39 La fonction reduce()

La fonction reduce() est une notion qui vient de la programmation fonctionnelle, comme map ou grep.

L’id´ee est assez simple : soit une fonction f() prenant deux param`etres, il s’agit d’appliquer cette fonction

`

a une liste de param`etres. On r´eduit la liste en appliquant successivement la fonction f() aux deux premiers

´

el´ements de la liste et en les rempla¸cant par le r´esultat. On continue jusqu’`a ce que la liste ne contienne plus qu’un seul ´el´ement, le r´esultat final.

Un exemple concret est celui de la somme, qui g´en´eralise l’addition (op´eration appliqu´ee `a deux op´erandes)

`

a une liste de plusieurs op´erandes.

Dans le cas g´en´eral, la r´eduction de la liste (a, b, c, d, e) par la fonction f() serait f( f( f( f( a, b ), c ), d ), e ).

Perl ne dispose pas d’une fonction reduce() en standard (contrairement `a Python, par exemple). Heureu- sement, le module List : :Util en propose une, qui s’utilise en passant un bloc de code en premier param`etre, exactement comme la fonction standard sort().

List : :Util fait partie de la distribution Scalar-List-Utils, qui contient ´egalement Scalar : :Util. Ces deux modules font partie de la distribution standard de Perl depuis la version 5.7.3.

Comme List : :Util fournit d´ej`a une fonction sum(), nous allons ´ecrire une fonction mul() qui calcule le produit des ´el´ements d’une liste :

use List::Util qw( reduce );

sub mul { reduce { $a * $b } @_ }

Tout l’int´erˆet de la fonction reduce() de List : :Util est de pouvoir utiliser les variables globales standard aetb, comme avec sort().

En effet, on peut sinon ´ecrire tr`es facilement l’´equivalent du code pr´ec´edent : sub mul { my $res = shift; $res = $res * $_ for @_; $res } Ceci est bien sˆur valable quelle que soit la fonction f() que l’on souhaite r´eduire.

Il suffit d’´ecrire$res = f( $res, $_ ) for @_ dans l’exemple pr´ec´edent.

Attention tout de mˆeme aux effets de bords, en particulier avec l’utilisation de shift(), qui enl`eve le premier ´el´ement de la liste. Dans un contexte plus large qu’une simple fonction de quelques lignes o`u on manipule @_,il faut faire attention `a ne pas modifier le tableau en question (ou au moins savoir qu’on le fait). Ainsi, `a la place de :

my $res = shift @liste; # ATTENTION, modifie la liste !

$res = f( $res, $_ ) for @liste;

on pr´ef`erera par exemple ´ecrire : my $res = $liste[0];

$res = f( $res, $_ ) for @liste[ 1 .. $#liste ];

(17)

ou toute autre version adapt´ee `a la fonction f() et `a l’utilisation que l’on fait du tableau @liste.

Pour information, le module List : :Util fournit ´egalement les fonctions suivantes :

* min LISTE et max LISTE

Le minimum et le maximum d’une liste de nombres. * minstr LISTE et maxstr LISTE Le minimum et le maximum d’une liste de chaˆınes de caract`eres. * first BLOC LISTE Le premier ´el´ement de la liste pour lequel le bloc renvoie une valeur vraie. * sum LISTE La somme des ´el´ements de la liste, l’exemple classique. * shuffle LISTE

Renvoie les ´el´ements de la liste dans un ordre al´eatoire.

40 Minimum et maximum d’une liste

Perl ne dispose pas non plus des fonctions min() et max() pour obtenir le minimum et le maximum d’une liste.

Sans rentrer dans les d´etails, on peut dire que c’est probablement parce qu’il existe beaucoup de mani`eres de comparer plusieurs valeurs (en tant que nombres ou en tant que chaˆınes de caract`eres, en tenant compte ou non de la localisation, etc.). De plus, de telles fonctions sont finalement assez peu utilis´ees et en g´en´eral courtes `a coder (comme nous l’avons vu avec reduce()) ; il n’a probablement pas ´et´e jug´e utile de gaspiller

un mot-cl´e pour elles.

C’est pourquoi le jour o`u on a besoin du maximum ou du minimum d’une liste (et pas de toute la liste tri´ee, auquel cas on utilise sort(), bien sˆur), il va nous falloir ´ecrire la fonction nous-mˆemes. Dans les exemples qui suivent, nous prendrons pour simplifier le maximum num´erique d’un tableau, mais c’est ´evidemment la mˆeme chose quelle que soit la liste `a traiter et la fonction de comparaison.

Commen¸cons par la fausse bonne id´ee :

sub max { (sort { $a <=> $b } @_)[-1] } # MAUVAIS

Le r´esultat est juste : on prend le dernier ´el´ement d’une liste tri´ee dans l’ordre croissant, c’est-`a-dire le maximum. C’est facile `a ´ecrire, ¸ca utilise un idiome Perl (indice n´egatif d’une liste), mais c’est tr`es mauvais en performance : en effet, on trie la liste toute enti`ere pour n’en garder qu’un seul ´el´ement.

L’algorithme de tri utilis´e par Perl d´epend des versions (il y a eu pas mal d’ajouts pour Perl 5.8, en particulier la possibilit´e avec la pragma sort de choisir l’algorithme de tri utilis´e), mais il donne au mieux un r´esultat en O(n log(n)).

Pour obtenir le maximum d’une liste, on va plutˆot utiliser la m´ethode classique, qui consiste `a d´ecr´eter que le maximum est le premier ´el´ement de la liste, puis `a parcourir la liste pour mettre `a jour sa valeur `a chaque fois qu’on rencontre un ´el´ement plus grand que le maximum en cours.

sub max { my $max = shift; $_ > $max and $max = $_ for @_; $max }

Cette m´ethode est en O(n), c’est `a dire que le nombre d’op´erations est proportionnel au nombre d’´el´ements de la liste. On ne peut pas faire mieux algorithmiquement. Plus le nombre n d’´el´ements de la liste croˆıt, meilleur sera cet algorithme par rapport au pr´ec´edent.

Nous avons trouv´e le meilleur algorithme, est-ce `a dire qu’il n’est pas possible de faire mieux ? Bien sˆur nous pouvons mieux faire, mais le gain obtenu ne pourra ˆetre que de l’ordre d’un facteur multiplicatif.

Ainsi, le module List : :Util vu pr´ec´edemment fournit une fonction max() ´ecrite en C. Sur mon syst`eme, celle-ci est environ 3 fois plus rapide que la version Perl pr´esent´ee ci-dessus. Certes, trouver le maximum d’une liste est d’autant plus long que la liste est grande, mais la fonction max() de List : :Util reste toujours

`

a peu pr`es 3 fois plus rapide que la version pr´ec´edente sur une liste de taille donn´ee.

A propos de List : :Util, nous pourrions nous servir de la version Perl de reduce() pr´` esent´ee dans la perle pr´ec´edente. La fonction qui donne le maximum de deux ´el´ements, tout le monde la connaˆıt : qui n’a pas vu les sempiternelles macros min et max en C ?

#define max(a,b) ((a)>(b)?(a):(b))

(18)

On pourrait donc ´ecrire une version un peu diff´erente de max(), comme ceci :

sub max { my $max = shift; $max = $_ > $max ? $_ : $max for @_; $max }

Il va falloir comparer les temps d’ex´ecution de ces fonctions pour estimer les performances des quatre versions de max() dont nous disposons d´esormais. Nous pouvons d’ores et d´ej`a faire quelques pr´edictions :

– Les versions utilisant l’algorithme en O(n) finiront toujours par ˆetre plus rapides que la version en O(n log(n)).

– La version C de List : :Util sera plus rapide que les versions Perl.

– La version Perl utilisant la formule $_ > $max and $max = $_ sera plus rapide que celle utilisant

$max = $_ > $max ? $_ : $max.

– En effet, la premi`ere formule fait une comparaison et ´eventuellement une affection (une fois le maximum trouv´e, plus aucune affectation ne sera faite), tandis que la seconde fait `a chaque fois une comparaison et une affection, ce qui est n´ecessairement plus coˆuteux.

41 Compter le nombre de lignes dans une chaˆıne

Un uniligne pour compter le nombre de lignes dans une chaˆıne :

$nr++ while "un\ndeux\ntrois\n" =~ m/\G.*?\n/gc;

A chaque it´eration, on part de la fin du match pr´ec´edent grˆace `a l’ancre \G, puis on saute un minimum de caract`eres grˆace `a .* ? avant de chercher un saut de ligne. On incr´emente alors $nr. On sort de la boucle quand on ne trouve plus de match.

Bien sˆur, en Perl, on peut proc´eder de multiples autres mani`eres pour arriver au mˆeme r´esultat : grep { $nr++ if $_ eq ’\n’} split ’’, "un\ndeux\ntrois\n";

$nr = grep { $_ eq ’\n’ } split ’’, "un\ndeux\ntrois\n";

$s = "un\ndeux\ntrois\n"

$nr = grep { substr($s, $_, 1) eq ’\n’} for 0..length($s)-1

42 La fonction pos()

En dehors du match par une regex, la position courante dans une chaˆıne est accessible par la fonction pos(). Comme beaucoup de fonctions Perl, elle prend la variable$_comme argument par d´efaut.

Illustrons par un exemple :

$s = "Les mongueurs de Perl connaissent bien le langage Perl";

# Affiche 21, la position apr`es la premi`ere occurrence de "Perl"

$s =~ m/Perl/gc ; print pos($s),"\n" ;

# Affiche toujours 21 car pas de match mais pas de remise `a z´ero

# `a cause de la pr´esence de l’option /c

$s =~ m/Python/gc ; print pos($s),"\n";

# Affiche 54, la position apr`es la seconde occurrence de "Perl"

$s =~ m/Perl/gc ; print pos($s),"\n" ; # affiche "54\n"

(19)

# Affiche 0. Pas de match et remise `a z´ero car absence de l’option /c.

# pos($s) retourne undef qui, utilis´e en contexte entier par

# l’addition du 0, est converti en 0.

$s =~ m/Python/g ; print pos($s)+0, "\n";

Dans la suite nous nous passerons de=~,car nous effectuerons la recherche dans$_.

Illustrons l’idiome m/\G.../gc par l’´ecriture d’un analyseur na¨ıf de fichier de configuration qui permet de remplir le hash%configavec des couples cl´e/valeur de configuration.

Ainsi un fichier .myconfig contenant : a = toto

b = titi c = tutu

reviendra `a initialiser%configcomme suit :

$config{’a’} = "toto";

$config{’b’} = "titi";

$config{’c’} = "tutu";

43 D´ ecoupage en tranches

Puisque l’objet de ce collier de perles est de pr´esenter des idiomes, rappellons que nous aurions pu exprimer la mˆeme chose en terme de tranches de hash :

@config{ ’a’, ’b’, ’c’ } = ( ’toto’, ’titi’, ’tutu’ ) que nous pouvons aussi ´ecrire en utilisant qw() pour cr´eer les listes :

@config{ qw( a b c ) } = qw( toto titi tutu );

Voici le script de lecture du fichier de configuration :

my %config; # hash qui contiendra la configuration open I, ".myconfig" or die $!;

while(<I>) {

s/[\s;]+//g; # supprime blancs et ´eventuels points virgules

$config{$1} = $2 if m/\G(\w+)=(\w+)/gc;

last if m/\G$/gc; # ´equivalent `a : last if pos == length }

Troisi` eme partie

Annexes

44 Extrait l’en-tˆ ete d’un mail

perl -pe ’/^$/ && exit’ mail.txt

(20)

45 Extrait le corps d’un mail :

perl -ne ’/^$/...do{print;0}’ mail.txt

46 Supprime la plupart des commentaires d’un source C

perl -0777 -pe ’s{/\*.*?\*/}{}gs’ source.c

47 Trouve le premier UID non utilis´ e

perl -le ’$i++ while getpwuid($i); print $i’

48 Num´ erote les lignes d’un fichier

perl -pe ’$_ = "$. $_"’ fichier

49 Conversion de secondes

Vous avez une dur´ee exprim´ee en secondes, mais vous voudriez l’afficher en jours, heures, minutes, se- condes.

$ perl -e ’$s=shift;print join"

",map{$i=int($s/$_->[0]);$s-=$i*$_->[0];chop$_->[1]

if$i==1;$i?($i,$_->[1]):()}[86400,"days"],[3600,"hours"], [60,"minutes"],[1,"seconds"]’ 120983

Code d´epli´e et comment´e :

$s = shift;

print join " ", map { # concat`ene le r´esultat avec des espaces

$i = int( $s / $_->[0] ); # combien de cette unit´e ?

$s -= $i * $_->[0]; # secondes restantes

chop $_->[1] if $i == 1; # supprime le ’s’ final au singulier

$i ? ( $i, $_->[1] ) : () # retourne les ´el´ements `a afficher }

# la liste des correspondances secondes/unit´e

[ 86400, "jours" ], [ 3600, "heures" ], [ 60, "minutes" ], [ 1, "secondes"]

50 Retrouvez votre adresse IP

$ perl -MLWP::Simple -le ’print get("http://whatismyip.com/")=~/IP\s+is ([\d.]+)/i’

51 Tester un compte POP

#!/usr/bin/perl use Net::POP3;

(21)

print STDERR "usage: pop3check server login [password]\n"

and exit unless @ARGV;

$| = 1;

my ($server,$login,$passwd) = @ARGV;

print "Password: " and chomp($passwd = <STDIN>) unless defined $passwd;

print "connecting to $server.. ";

my $pop = Net::POP3->new($server);

print STDERR "can’t connect to server\n" and exit unless defined $pop;

print "ok\n";

$pop->login($login, $passwd);

print STDERR "error: wrong username or password\n" and exit unless $pop->ok;

my ($undeleted, $size) = $pop->popstat;

my $last = $pop->last;

print "mail box size: $size\n",

"$undeleted unread mail(s).\n",

"last read mail was number $last\n\n";

52 G´ en´ erer toutes les adresses IP de plusieurs sous-r´ eseaux

#!/usr/bin/perl use NetAddr::IP;

print STDERR "usage: subnets network/mask bits\n" and exit unless @ARGV;

my($network,$bits) = @ARGV;

my $mask = (split ’/’, $network)[1];

print STDERR "bits undefined or smaller than mask\n" and exit unless $bits >= $mask;

for my $net ( NetAddr::IP->new($network)->split($bits) ) { print join(" ", map { $net+$_ } 0..(1<<(32-$bits))-1 ), $/

}

53 G´ en´ erer une liste de nombres

$ perl -le print,for+shift..shift 1 10

54 Valeurs hexad´ ecimales des nombres de 27 ` a 33

$ perl -e ’printf$ARGV[0].$/,$_ for+shift..shift’ 27 33 "%02X"

55 G´ en´ erer une bˆ ete liste de nombres

$ perl -e ’$ARGV[2]||="%s";printf$ARGV[0].$/,$_ for+shift..shift’ DEBUT FIN FORMAT

(22)

56 L’idiome substr() = ”toto”

Il est peu connu que la fonctionsubstr()peut ˆetre lhs. Ce sigle pour left hand side signifie qu’une expression peut apparaˆıtre dans la partie gauche d’une affectation.

On sait quesubstr($str, $debut, $longueur)retourne la sous-chaˆıne de$strde longueur$longueur commen¸cant `a la position$debut. Mais, en mettant cette expression en lhs, cette sous-chaˆıne est remplac´ee par la partie droite de l’affectation. Exemple :

$s = "groupe de mongers parisiens";

print substr($s, 10, 7); # affiche "mongers"

substr($s, 10, 7) = "mongueurs";

print $s; # affiche "groupe de mongeurs parisiens";

Notons que la fonction pos() est aussi lhs de sorte que vous pouvez modifier la position courante dans une chaˆıne.

57 Visualisation de la progression

Revenons `a notre script. Notre analyse syntaxique se bloque si le fichier de configuration n’a pas le format attendu. Elle boucle alors ind´efiniment. Corrigeons cela. En cas d’erreur, le script indiquera la position de l’erreur, puis sortira. On le fait en ins´erant comme marqueur la chaˆıne "<*>" `a la position courante de la chaˆıne analys´ee. On sort en affichant cette chaˆıne modifi´ee si son analyse ne progresse plus. Adaptons notre script pour afficher la position courante pour ce faire.

Nous incluons aussi Data : :Dumper pour pouvoir afficher la valeur de%config`a la fin du script.

use strict;

use Data::Dumper

my %config; # hash qui contiendra la configuration open I, ".myconfig" or die $!;

while(<I>) {

my $pos = pos; # pos() m´emorise la position courante s/[\s+;]+//g;

$config{$1} = $2 if m/\G(\w+)=(\w+)/gc;

last if m/\G$/gc;

if ( $pos == pos ) { # la position courante a-t-elle avanc´e ? substr( $_, pos, 0 ) = "<*>";

die $_; # meurt si on n’a pas avanc´e dans la cha^ıne }

}

print Dumper(\%config);

58 Les parenth` eses ne font pas les listes

Notons que, dans notre script ci-dessus, nous appellons la fonction pos() sans utiliser de parenth`eses. En perl, dans l’´ecriture de l’appel d’une fonction, les parenth`eses ne sont l`a que pour grouper les ´el´ements d’une liste, ´eventuellement vide, de param`etres. En d’autre termes, l’op´erateur de cr´eation de liste est la virgule.

(23)

Ce groupement par les parenth`eses est souvent n´ecessaire car la pr´ec´edence de l’op´erateur d’affection est plus forte que celui de cr´eation de liste. Ainsi les parenth`eses sont indispensables dans l’expression :

substr( $_, pos, 0 ) = "<*>";

Car :

substr $_, pos, 0 = "<*>";

est l’´equivalent de :

substr( $_, pos, (0 = "<*>") );

Cela n’a pas de sens car comme le compilateur le signalera alors, une constante ne peut pas ˆetre en position lhs.

59 D´ ecouper un fichier diff (une rustine, quoi)

Pour produire un patch, il faut faire un diff. La commande suivante produit un fichier contenant l’int´e- gralit´e des diff´erences entre les fichiers des deux arborescences pass´ees en param`etre.

$ diff -Nru projet.new/ projet.HEAD/ > projet.patch

Le programme patch (´ecrit `a l’origine par un certain Larry Wall) sait lire ce fichier rustine pour en appliquer le r´esultat `a l’arborescence d’origine.

Si vous voulez r´ecup´erer les rustines individuelles (fichier source par fichier source), vous pouvez utiliser l’uniligne suivant :

$ perl -MIO::File -pe ’*STDOUT=IO::File->new(sprintf"> patch.%03d", ++$i) if /^diff/’

On profite de la boucle implicite cr´e´ee par l’option -p pour lire le fichier de patch ligne `a ligne et imprimer automatiquement chaque ligne sur la sortie standard (STDOUT). L’astuce consiste `a changer le fichier correspondant `a STDOUT `a chaque fois qu’on d´etecte le d´ebut d’un nouveau diff.

L’interface fournie par le module standard IO : :File et sa m´ethode new permet de retourner un filehandle

`

a partir d’un nom de fichier, IO : :File s’´etant charg´e d’ouvrir le fichier. Or un filehandle est la seule chose que l’on puisse affecter `a un glob (au sens de perl) tel que *STDOUT. C’est ce qui est fait.

Pour ceux qui s’inqui`etent de l’utilisation des ressources, sachez que les fichiers sont automatiquement ferm´es lors de l’association de STDOUT au fichier. Cela a ´et´e v´erifi´e grˆace `a la commande lsof(1). Maintenant que nous connaissons le principe de base, imaginons que, en plein s´eance de compilation de RPM, nous mo- difions les sources en live dans~/rpm/BUILD/package/, avec une arborescence de r´ef´erence dans~/package.

Les fichiers dans ~/rpm/BUILD ´etant effac´es `a chaque recompilation par rpmbuild -ba package.spec, nous tenons `a obtenir sous forme de patch (le format n´ecessaire `a RPM) nos modifications.

Le r´eflexe premier est de faire un gros diff :

$ diff -urN ~/package/ ~/rpm/BUILD/package/ | grep -v ^Binary > ~/tmp/mongros.patch D´ej`a, on s’aper¸coit que diff rencontre des fichiers binaires dont il ne sait que faire (d’o`u le grep), mais il va aussi rencontrer tout ce qui fichier text´e cr´e´e par configure, comme les Makefile, fichiers de d´ependance, etc.

Le patch va donc ˆetre ´enorme, avec un quantit´e industrielle de d´echets (essayez).

Or, ce qui nous int´eresse, ce sont essentiellement les fichiers .c et .h qui ont ´et´e modifi´es. Perl `a la rescousse :

$ perl -MIO::File -pe ’if(/^diff/){$n=m!.*/(.*\.[ch])$! ? ">$1.patch" : ">/dev/null" ;

*STDOUT=IO::File->new($n)}’ mongros.patch

(24)

L`a, ayant construit le nom de fichier ($n) `a ouvrir(*STDOUT=IO::File->new($n)) `a partir des noms des fichiers((.*\.[ch])$)dans le diff, on obtient les trois patchs sur 50 qui nous int´eressent :

$ echo *.patch

check_disk.c.patch check_smtp.c.patch check_ups.c.patch

Notez l’utilisation de l’op´erateur m// sous sa forme m ! !, pour deux raisons : si on avait gard´e la forme m//, il nous aurait fallu ´echapper le / dans l’expression rationnelle, pour ´eviter que perl ne le confonde avec la fin de l’expression ; et comme le shell utilise le mˆeme caract`ere que perl pour les ´echappements(\), il nous aurait fallu l’´echapper deux fois(\\/). Les 47 rustines qui ne nous int´eressent pas sont poubellis´ees grˆace `a ce cher/dev/null, bien pratique `a utiliser.

Il nous faut n´eanmoins rajouter un test suppl´ementaire au d´ebut, de fa¸con `a ne r´eouvrir un nouveau fichier qu’`a la ligne commen¸cant par/^diff/.Sinon, vos patches n’auront qu’une ligne, et leur contenu sera parti `a la poubelle.

Il ne nous reste plus qu’`a concat´ener nos trois fichiers pour avoir un joli patch `a int´egrer `a notre package.spec:

$ cat *.patch > monpetit.patch

Une autre solution est de tout concat´ener grˆace `a Perl :

$ perl -MIO::File -pe ’if(/^diff/){$n=m!.*/(.*\.[ch])$!?">>$ARGV.petit":">/dev/null";

*STDOUT=IO::File->new($n)}’ mongros.patch L`a,$ARGV est utilis´e pour r´ecup´erer le nom du fichier lu par l’op´erateur diamant<>, lui-mˆeme induit par le commutateur -p pass´e `a perl. Vous trouverez plus d’informations en consultant les pages de manuel perlrun(1)et perlvar(1).

Ah, au fait, pourquoi faire compliqu´e quand on peut faire simple ? Notre ligne de commande commence

`

a s´erieusement s’allonger, allons la raccourcir en utilisantopen:

$ perl -pe ’if(/^diff/){$n=m!.*/(.*\.[ch])$!?">>$ARGV.petit":">/dev/null";open STDOUT,$n}’

mongros.patch

¸

Ca fait quelques 23 caract`eres de gagn´es, non n´egligeables pour les fain´eants que nous sommes.

60 R´ ecup´ erer ses mails

Avec un titre pareil, vous allez vous dire que ¸ca part mal : pour r´ecup´erer ses mails, on utilise son client mail (quel qu’il soit), et ¸ca marche tr`es bien. Exact, je pr´ef`ere ¸ca aussi. Mais r´ecemment, suite `a un d´em´enagement, je me suis retrouv´e coup´e de tout acc`es au net, et donc dans l’impossibilit´e de r´ecup´erer mes mails. Or je re¸cois environ 200 mails par jour et autant de spam. Et le quota sur Free n’est que de 25 Mo.

Donc au bout de d’un mois, mon compte s’est dangereusement rapproch´e de la limite sup´erieure, et il me fallait r´ecup´erer mes mails avant que les suivants ne soient refus´es. La r´eponse toute faite de la plupart des personnes est d’utiliser Fetchmail. Sauf que Fetchmail tient absolument `a renvoyer les mails sur un serveur qui se chargera de les d´elivrer (un MDA, Mail Delivery Agent). C’est une solution, mais je voulais simplement r´ecup´erer mes mails, les stocker tous dans un simple fichier au classique format mbox. A priori, Fetchmail ne permet pas de faire ¸ca. Voici donc un petit script Perl pour r´ecup´erer les mails par POP3.

#!/usr/bin/perl use strict;

use Email::Simple;

use Net::POP3;

sub usage { die "usage: getmail file\n" }

(25)

my $server = ’pop.free.fr’;

my $login = ’maddingue’;

my $passwd = ’5eckr3t’;

my $mbox = shift or usage();

$| = 1;

print "connecting to $server.. ";

my $pop = new Net::POP3 $server

or die "error: can’t connect to $server: $!\n";

print "ok\n";

$pop->login($login, $passwd);

$pop->ok or die "error: wrong username or password\n";

my ($undeleted, $size) = $pop->popstat;

my $last = $pop->last;

print "mail box size: $size\n",

"$undeleted unread mail(s).\n",

"last read mail was number $last\n\n";

open(MBOX, ’>’, $mbox) or die "error: can’t write ’$mbox’: $!\n";

my $fetched = 0;

for my $num (1..$undeleted) { my $msg = $pop->get($num);

next unless ref $msg;

mbox_envelope($msg);

print MBOX @$msg, $/;

$fetched += $pop->list($num);

printf "\rfetched %2.0f%%", $fetched*100/$size;

$pop->delete($num);

}

close(MBOX);

print $/;

$pop->quit;

sub mbox_envelope { my $text = $_[0];

my $msg = new Email::Simple join ’’, @$text;

my $date = $msg->header(’Date’);

my $from = $msg->header(’Return-Path’);

$from = $msg->header(’From’) unless $from;

$from =~ s/[<>]//g;

$from =~ /(\S+\@\S+)/ and $from = $1;

unshift @$text, "From $from $date\n"

}

Vous reconnaˆıtrez dans le d´ebut du script celui pr´esent´e il y a un an et demi pour v´erifier son compte POP3.

Il est augment´e d’une boucle qui r´ecup`ere les messages l’un apr`es l’autre et les stocke dans le fichier dont le

(26)

nom a ´et´e donn´e en argument du script. D´etaillons son d´eroulement.

Apr`es s’ˆetre connect´e($pop = new Net::POP3 $server),authentifi´e($pop->login($login, $passwd)) et avoir r´ecup´er´e le nombre de mails `a lire($pop->popstat),une boucle se charge de traiter chaque mes- sage. `A noter qu’elle commence `a 1 et non 0. On t´el´echarge chaque message avec$pop->get($num), qui le renvoie sous la forme d’une r´ef´erence `a tableau de lignes. On le passe `a la fonctionmbox_envelope() dont le rˆole est d’ajouter une ligne au format From EXPEDITEUR DATE.

Cette ligne, dite d’enveloppe, contient l’adresse de l’exp´editeur telle qu’elle a ´et´e donn´ee au serveur mail d’envoi avec la commande SMTP MAIL FROM :, suivie de la date d’envoi. On la reconstitue en prenant la valeur du champ Return-Path :, s’il est pr´esent, qui contient justement cette adresse, et sinon en prenant celle du champ From :. Cela peut sembler inutile mais cette ligne d’enveloppe, qui pr´ec`ede les entˆetes RFC-822, est n´ecessaire pour que le fichier soit au format mbox et que les clients mails puissent ensuite le lire.

Cette ligne est ensuite ins´er´ee en d´ebut du tableau qui contient le message. Puis celui-ci est stock´e dans le fichier, et le message est marqu´e pour destruction sur le serveur POP3. `A noter que les messages ne sont effectivement d´etruits que lorsqu’on ex´ecute$pop->quit(),donc jusqu’`a ce moment-l`a, le script peut `a tout moment ˆetre interrompu sans que cela n’affecte vos mails sur le serveur.

On peut noter que ce script utilise, en plus du module Net : :POP3, le module Email : :Simple du projet PEP[1] (Perl Email Project). Ce projet initi´e par Simon Cozens consiste `a fournir des modules plus propres et plus simples que ceux qui existaient avant dans Mail : :* (y compris les siens). Il faut reconnaˆıtre qu’ici, son nom en : :Simple n’est pas abusif puisque l’interface est tr`es naturelle : on passe le message en argument de new(), et on peut r´ecup´erer chaque entˆete avec la m´ethode header(). La prochaine fois que vous avez besoin d’un module Perl pour manipuler les mails, je vous recommande donc tr`es chaudement de regarder d’abord les modules du projet PEP, qui sont v´eritablement simples `a utiliser, mˆeme s’ils souffrent parfois d’un certain manque de documentation.

Enfin, pour ceux qui se demanderaient si j’ai vraiment utilis´e ce script, je r´eponds oui, et mˆeme plus d’une fois. Au total, j’ai ainsi pu r´ecup´erer les quelques 6000 mails (hors spam) qui se sont accumul´es en deux mois sur mon compte.

61 Un (autre) robot de traduction

Nous avons d´ej`a pr´esent´e dans Linux Mag 61 un traducteur automatique, qui allait chercher les traduc- tions de Babelfish `a l’aide d’un module CPAN. Voici aujourd’hui un rapide robot de traduction qui s’appuie cette fois sur le site FreeTranslation (http ://www.freetranslation.com/).

Comme toujours, une fois trouv´ee la page contenant le formulaire ad´equat, nous demandons `a voir le formulaire dans ses moindres d´etails :

$ mech-dump http://www.freetranslation.com/free/

GET http://www.freetranslation.com/search/ [frmSearch]

q=Search... (text)

<NONAME>=Search (submit)

POST http://ets.freetranslation.com/ [frmTranslator]

sequence=core (hidden readonly)

mode=html (hidden readonly)

charset=UTF-8 (hidden readonly)

template=results_en-us.htm (hidden readonly)

language=English/Spanish (option) [*English/Spanish/English to Spanish|...|

Russian/English/Russian to English]

srctext=Type or paste some text here. (textarea) HumanTranslation=<UNDEF> (button)

Submit=FREE Translation (submit)

(27)

C’est ici le second formulaire qui nous int´eresse. Les noms des champs sont suffisamment parlants pour que nous identifiions rapidement les champs utiles : language et srctext. Un premier essai nous montre que la r´eponse est ´egalement dans un des champs du formulaire, le champ dsttext.

Le script est constitu´e d’une boucle simple qui lit l’entr´ee standard ligne `a ligne, envoie chaque ligne au site de traduction et affiche le r´esultat, avant de re-pr´esenter le prompt, pour recommencer :

#!/usr/bin/perl use strict;

use WWW::Mechanize;

my $m = WWW::Mechanize->new();

$|++; # autoflush

# charge la premi`ere page

$m->get(’http://www.freetranslation.com/free/’);

die $m->res()->status_line() . "\n" unless $m->success();

print "? ";

while (<>) {

# s´electionne le second formulaire

$m->form_number(2);

# ou ’French/English’, ’English/German’, ’Italian/English’

$m->field( language => ’English/French’ );

$m->field( charset => ’iso-8859-1’ ); # voir ci-dessous

$m->field( srctext => $_ );

$m->click();

print $m->current_form()->value(’dsttext’);

print "\n? ";

}

Nos tests montrent rapidement qu’on peut ´egalement utiliser le champ charset si on pr´ef`ere iso-8859-1 plutˆot que le d´efaut UTF-8 (d’o`u la ligne suppl´ementaire dans mon script).

Et ¸ca marche !

? programming language langage de programmation

? the three virtues of a programmer are impatience, lazyness and hubris

les trois vertus d’un programmeur sont des impatiences, lazyness et la pr´etention Enfin, aussi bien que peut marcher la traduction automatique... ;-)

Il s’agit d’un petit script rapide, mais c’est un bon point de d´epart pour ´ecrire le module plus g´en´erique (par exemple Lingua : :Translate : :FreeTranslation).

62 Mesurer son d´ ebit avec l’aide de Free

Sur la page http ://tdebit.proxad.net/debit/ le fournisseur d’acc`es Free fournit un test de d´ebit pour mesurer les d´ebits montants et descendants disponibles sur votre connexion.

Une fois la page t´el´echarg´ee, on voit que le script est en fait charg´e dans un<iframe>:

<iframe align=center frameborder=0 WIDTH=100% HEIGHT=300 src="index.pl">

(28)

Nous utilisons mech-dump pour aller r´ecup´erer le formulaire directement et l’analyser :

$ mech-dump http://tdebit.proxad.net/debit/index.pl

POST http://tdebit.proxad.net/debit/debit.pl (multipart/form-data)

ok=submit (image)

up=010000001001000...100000010 (hidden readonly)

dureeup=6.0342 (hidden readonly)

sizeup=679209 (hidden readonly)

Le contenu du champ up est ´enorme : 79521 caract`eres ! Cela fait partie de l’algorithme de calcul : ces donn´ees vont ˆetre envoy´ees lors du POST effectu´e lorsque que nous cliquerons sur le bouton Lancer le test de d´ebit , afin de calculer un d´ebit `a l’aide du temps mis par le script de Free pour recevoir ces donn´ees (calcul du d´ebit montant).

Les deux champs sizeup et dureeup, contrairement `a ce que semblent indiquer leurs noms sont associ´es au calcul de d´ebit descendant. Ils correspondent respectivement au volume de donn´ees re¸cues (cach´ees dans des commentaires HTML) lors de la r´eception du formulaire et au temps qu’il a fallu au script pour les envoyer

`

a notre client.

Construire un script qui valide le formulaire et r´ecup`ere la page HTML g´en´er´ee prend quelques lignes :

#!/usr/bin/perl use WWW::Mechanize;

my $m = WWW::Mechanize->new( autocheck => 1 );

$m->get(’http://tdebit.proxad.net/debit/index.pl’);

$m->click(’ok’);

print $m->content;

Le contenu affich´e contient toutes les informations souhait´ees :

<td class=’SMALL’><p><b><font color=’#61718A’><b>D´ebit descendant (download)</b></font></b><br>

Taille du fichier 604,51 ko<br>

Dur´ee 5.426 secondes<br>

<b>D´ebit 891,25 kbit/s</b>

(111,41 ko/s)

<br><br><img src=’blank.gif’ height=15 width=213.648043847452 align=center>

891,25 kbit/s <br><img src=’echelleup.gif’><br></p><p><b><font color=’#61718A’>

D´ebit montant (upload)</font></b><br>

Taille du fichier 75,57 ko<br>

Dur´ee 2.236 secondes<br>

<b>D´ebit 270,4 kbit/s</b>

(33,8 ko/s)<br>

Et il ne nous reste plus qu’`a les extraire.

my @data = $m->content() =~ m{

Taille\ du\ fichier\ (\d+(?:,\d+)?\ ko).*?

Dur´ee\ (\d+(?:\.\d+)?\ secondes).*?

D´ebit\ (\d+(?:,\d+)?\ kbit/s).*?

\((\d+(?:,\d+)?\ ko/s)\) }gsx;

Avec cette expression r´eguli`ere, nous r´ecup´erons les 8 valeurs d’un seul coup dans notre tableau. Nous devons prot´eger les espaces contenus dans le texte (ou les remplacer par \s)`a cause de l’utilisation de l’option /x pour l’expression r´eguli`ere.

(29)

Nous avons ´egalement utilis´e ( ? :...) ? pour rendre optionnels les chiffres apr`es la virgule (ou le point).

Une derni`ere remarque : `a cause des accents dans l’expression r´eguli`ere et de l’encodage des donn´ees re¸cues depuis le script de Free (iso-8859-1), il faut imp´erativement que le script soit encod´e en iso-8859-1.

Le tableau obtenu `a l’aide de cette expression r´eguli`ere correspond `a :

@data = (

# d´ebit descendant

’604,51 ko’, # taille du fichier

’5.426 secondes’, # dur´ee de transfert

’891,25 kbit/s’, # d´ebit en kbit/s

’111,41 ko/s’, # d´ebit en ko/s

# d´ebit montant

’75,57 ko’, # taille du fichier

’2.236 secondes’, # dur´ee de transfert

’270,4 kbit/s’, # d´ebit en kbit/s

’33,8 ko/s’ # d´ebit en ko/s );

En ajoutant une petite boucle d’affichage, on obtient le script suivant :

#!/usr/bin/perl use WWW::Mechanize;

my $m = WWW::Mechanize->new( autocheck => 1 );

# n´ecessaire pour ´eviter que Free filtre selon les navigateurs

$m->agent_alias( ’Linux Mozilla’ );

$m->get(’http://tdebit.proxad.net/debit/index.pl’);

$m->click(’ok’);

my @data = $m->content() =~ m{

Taille\ du\ fichier\ (\d+(?:,\d+)?\ ko).*?

Dur´ee\ (\d+(?:\.\d+)?\ secondes).*?

D´ebit\ (\d+(?:,\d+)?\ kbit/s).*?

\((\d+(?:,\d+)?\ ko/s)\) }gsx;

my $i = 0;

for (qw( descendant montant )) { print "D´ebit $_ :\n",

" $data[$i+3] ($data[$i+2])\n",

" $data[$i] en $data[$i+1]\n";

$i += 4;

}

Qui affiche chez moi (T´el´e2 1024) : D´ebit descendant :

111,41 ko/s (891,25 kbit/s) 604,51 ko en 5.426 secondes D´ebit montant :

33,8 ko/s (270,4 kbit/s) 75,57 ko en 2.236 secondes

(30)

Merci `a DomiX d’avoir demand´e un coup de main sur le canal IRC des mongueurs(#perlfrsur le serveur irc.mongueurs.net) lors du d´ebogage de son propre script.

63 Fractionner une image

Pour la conf´erence YAPC : :Europe 2005, les organisateurs avaient d´ecid´e de fournir un maximum d’in- formation et de mat´eriel aux participants, quitte `a ce qu’il en ait trop. ;-)

Ainsi, ils ont mis `a disposition sur le site de la conf´erence[1] une premi`ere carte sous la forme d’une image au format PNG[2], qui est un plan de Braga avec des points num´erot´es pour rep´erer les diff´erents lieux relatifs `a la conf´erence. Une autre personne d´ecida alors de fournir l’´equivalent Google Maps, ce qui rendit la pr´ec´edente carte moins utile. N´eanmoins je d´ecidais d’essayer de l’imprimer pour en avoir une version papier sous la main une fois `a Braga.

Apr`es avoir r´ecup´er´e cette image (qui p`ese tout de mˆeme 5,1 Mo) je me suis demand´e comment l’imprimer, car un premier essai me confirma qu’une impression directe en A4 ´etait peu utile, le texte ´etant illisible. La solution ´etait donc de fractionner l’image en plusieurs parties afin d’imprimer chacune sur une feuille A4.

N’ayant pas envie de d´ecouper l’image `a la main , je commen¸cais `a chercher un m´ecanisme pour s’en charger pour moi.

Un rapide coup d’oeil dans The Gimp ne m’indiqua rien de flagrant pour r´ealiser cette op´eration. Je me tournait ensuite vers les commandes en ligne de l’autre couteau suisse en mati`ere d’images, ImageMagick.

convert(1) ne permet que de convertir une image en un autre format (avec la possibilit´e d’appliquer l´egion d’effets sp´eciaux). mogrify(1) permet de transformer des images, par exemple pour les redimensionner et montage(1) d’assembler plusieurs images en une seule, mais rien pour fractionner une image. Restait conjure(1), qui ex´ecute un script MSL (Magick Scripting Language), un machin un peu infˆame en XML.

Commen¸cant `a d´esesp´erer, je me mets `a chercher sur Freshmeat puis le CPAN, o`u je suis tomb´e sur le module Image : :Magick : :Tiler[3] de Ron Savage.

Celui-ci rend cette op´eration d’une simplicit´e d´econcertante. Jugez plutˆot, pour fractionner le plan de Braga en 6 carreaux , le petit script suivant suffit :

#!/usr/bin/perl use strict;

use Image::Magick::Tiler Image::Magick::Tiler->new(

input_file => shift || die("usage: $0 image [geometry [format]]"), geometry => shift || ’2x2’,

output_type => shift || ’png’, write => 1, verbose => 1, )->tile()

et il s’ex´ecute ainsi :

$ tiler braga.png 3x2

Image : :Magick : :Tiler cr´ee alors les fichiers correspondants :

$ ls -l total 10384

-rw-r--r-- 1 maddingue users 600908 nov 6 17:59 1-1.png -rw-r--r-- 1 maddingue users 930240 nov 6 17:59 1-2.png -rw-r--r-- 1 maddingue users 960254 nov 6 17:59 1-3.png -rw-r--r-- 1 maddingue users 852730 nov 6 17:59 2-1.png -rw-r--r-- 1 maddingue users 1171000 nov 6 17:59 2-2.png -rw-r--r-- 1 maddingue users 750563 nov 6 17:59 2-3.png -rw-r--r-- 1 maddingue users 5316276 jan 14 2005 braga.png

(31)

D´etaillons un peu le fonctionnement de ce script (mˆeme s’il est plutˆot simple).

On cr´ee un objet (Image : :Magick : :Tiler->new(...)) et on ex´ecute la m´ethode tile() qui effectue le travail proprement dit en fonction des param`etres pass´es `a new().

*

input_file attend ´evidemment le nom du fichier `a traiter. Ici, on utilise un court-circuit (l’op´erateur

||) pour soit r´ecup´erer le premier argument du script, soit terminer le script en affichant son usage. * geometry indique comment d´ecouper l’image de d´epart. L’argument est de la forme NxM+x+y, o`u N est le nombre par d´efaut de carreaux en horizontal, et M le nombre par d´efaut de carreaux en vertical. Si l’image de d´epart a une largeur L et une hauteur H, les carreaux ont donc par d´efaut une largeur de L / N et une hauteur de H / M. +x et +y permettent ensuite d’ajuster respectivement la largeur et la hauteur des carreaux, auquel cas Image : :Magick : :Tiler sera potentiellement amen´e `a augmenter ou diminuer le nombre de carreaux `a cr´eer. *

output_type permet d’indiquer le format de sortie, par d´efaut PNG. *

write indique `a la m´ethode tile() d’´ecrire les images sur disque au lieu de simplement cr´eer les objets Image : :Magick correspondants. *

verbose indique ´evidemment au module d’ˆetre verbeux.

A noter qu’il existe aussi un param`` etreoutput_dirpour indiquer le r´epertoire o`u cr´eer les images (par d´efaut dans le r´epertoire courant).

64 D´ ecouper des MP3 avec Perl

Le script final

Finalement, notre script est assez simple puisqu’il ressemble `a ce qui suit :

#!/usr/bin/perl use strict;

use warnings;

use Getopt::Long;

package My::MP3::Splitter;

use MP3::Splitter;

use Spreadsheet::Read qw( ReadData rows );

use Carp;

sub new {

my $class = shift;

my $self = bless {}, $class;

$self->{input_file} = shift if scalar @_ >= 1; # on v´erifie si

# l’utilisateur a pass´e un

# param`etre lors de la

# cr´eation de l’objet }

sub _process_input_file { my $self = shift;

if ( -e $self->{input_file} ) {

my $mp3_files = ReadData( $self->{input_file} );

my @files = rows($mp3_files->[1]);

(32)

shift @files; # par souci de documentation, la premi`ere ligne des

# fichiers trait´es est ignor´ee, permettant ainsi

# d’indiquer le type de donn´ees attendu foreach my $row (@files) {

# on passe si...

next if $row->[0] eq ""; # - cellule vide

next if not -e $row->[0]; # - le fichier MP3 n’existe pas next if scalar @{$row} < 4; # - pas assez d’information

$self->_split_file(@{$row});

} } else {

croak "Le fichier $self->{input_file} n’existe pas...";

} }

sub _split_file {

my ($self, $mp3_file, $new_file, $begin_part, $end_part) = @_;

my $duration = $self->_compute_duration($begin_part, $end_part);

mp3split($mp3_file, { name_callback => sub { $new_file } }, [ $begin_part, $duration ]);

}

sub _compute_duration {

my ( $self, $begin, $end ) = @_;

my ( $b_hour, $b_min, $b_sec )

= $begin

=~ /^(?:([\d.]+)(?:h|:(?=.*[m:])))?(?:([\d.]+)[m:])?(?:([\d.]+)s?)?$/;

for ( $b_hour, $b_min, $b_sec ) { next unless defined $_;

/^(\d+\.?|\d*\.\d+)$/;

}

my $begin_total

= ( $b_hour || 0 ) * 3600 + ( $b_min || 0 ) * 60 + ( $b_sec || 0 );

my ( $e_hour, $e_min, $e_sec )

= $end

=~ /^(?:([\d.]+)(?:h|:(?=.*[m:])))?(?:([\d.]+)[m:])?(?:([\d.]+)s?)?$/;

for ( $e_hour, $e_min, $e_sec ) { next unless defined $_;

/^(\d+\.?|\d*\.\d+)$/;

}

my $end_total

= ( $e_hour || 0 ) * 3600 + ( $e_min || 0 ) * 60 + ( $e_sec || 0 );

return $end_total > $begin_total ? $end_total - $begin_total : 0;

}

sub run {

my ($self) = shift;

if (scalar @_ >= 1) {

$self->{input_file} = shift; # on v´erifie si l’utilisateur a sp´ecifi´e

(33)

# un param`etre `a la fonction, et le cas

# ´ech´eant, on se pr´epare `a traiter ce

# fichier } else {

if (not defined $self->{input_file}) {

croak "No input file...\n"; # on g`ere le cas o`u aucun fichier `a

# traiter n’a ´et´e sp´ecifi´e. Que ce

# soit lors de la cr´eation de l’objet,

# ou lors de l’appel de la m´ethode }

}

$self->_process_input_file();

}

package main;

my %conf;

GetOptions( \%conf, "input=s" );

usage() if not exists $conf{input};

My::MP3::Splitter->new( $conf{input} )->run();

sub usage {

die "$0 --input file, or $0 -i file\n";

}

Conclusion

Voil`a, j’ai maintenant la possibilit´e d’extraire des morceaux de mes fichiers MP3. ´Evidemment, je pourrais encore am´eliorer les services que peut me rendre ce script, par exemple, en ajoutant des champs dans le fichier CSV, je pourrais ajouter des informations ID3 aux fichiers MP3 cr´e´es, mais je laisse la r´ealisation de cette id´ee au lecteur, ou `a une soir´ee prochaine.

R´ ef´ erences

[1] Sylvain Lhullier (2004)Introduction `a la programmation en Perl, ou comment d´ebuter en Perl.

[2] http ://articles.mongueurs.net/

Références

Documents relatifs

b) Le r´esultat d’une soustraction est une diff´ erence. c) Le r´esultat d’une multiplication est

perlrebackslash Les séquences backslash des expressions rationnelles perlrecharclass Les classes de caractères des expressions rationnelles perlreref Résumé rapide des

7.8 Ecrire une table de hachage sur disque avec les fichiers DBM.. 8.17

Il ne couvre pas tous les aspects du langage, sa lecture ne dispense pas de consulter les ouvrages de r´ef´erences cit´es dans la bibliographie.. Une version ´electronique

Later, we'll learn how to tell perl how many repetitions of a character or group of characters we want to match without spelling it out directly. What, then, if we wanted to

Exercice 4 Ecrire des proc´ ´ edures permettant le calcul du produit scalaire, de la norme eucli- dienne ainsi que de la norme infinie.. Exercice 5 On veut r´ esoudre un syst` eme

Reloading configuration files. First, lets look at a simple case, in which we just have to look after a simple configuration file like the one below. Imagine a script that tells you

[ Library Home | Perl in a Nutshell | Learning Perl | Learning Perl on Win32 | Programming Perl | Advanced Perl Programming | Perl Cookbook ]... Previous: What This Book