[couverture de Linux Magazine 63]

Perles de Mongueurs (5)

Article publié dans Linux Magazine 63, juillet/août 2004.

[+ del.icio.us] [+ Developers Zone] [+ Bookmarks.fr] [Digg this] [+ My Yahoo!]

Générer une liste de nombres

Les distributions GNU/Linux récentes comportent un outil peu connu, seq(1), qui permet de générer une liste de nombres successifs. Néanmoins, étant une innovation GNU, il n'est pas disponible sur les autres systèmes Unix (et non-Unix). On peut remédier à ceci en écrivant un outil similaire en Perl.

Une première version très simple peut s'écrire avec cet uniligne qui affiche les nombres de 1 à 10 :

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

C'est suffisant s'il s'agit juste de générer une liste de nombres, mais si le but est de s'en servir dans un format (pour générer une liste de noms successifs par exemple), on peut gérer cela directement dans Perl. Ainsi cet uniligne affiche les valeurs hexadécimales des nombres de 27 à 33 :

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

Cette seconde version impose de spécifier le format. Si on veut conserver la possibilité de générer une bête liste de nombres (comme dans le premier exemple), il suffit de rajouter un format par défaut :

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

Si on sauve cette commande dans un script seq.pl, L'exécution de

    $ seq.pl 1 7 'fichier.%03d'

affiche la liste des noms de "fichier.001" à "fichier.007". L'aspect intéressant est qu'on profite « gratuitement » de l'incrémentation magique de Perl, qui sait traiter les lettres en plus des nombres. Ainsi,

    $ seq.pl xaa xah

donne les noms des quelques premiers fichiers créés par split(1). Quelque chose que le seq(1) de GNU ne sait pas faire.

(Sébastien Aperghis-Tramoni (Maddingue), Marseille.pm - sebastien@aperghis.net)

glob

La fonction glob() sert en général à obtenir une liste de fichiers à partir d'un motif comme ceux utilisés par le shell csh :

    $ ls perles*.pod
    perles-01.pod
    perles-02.pod
    perles-03.pod
    perles-04.pod
    perles-05.pod

    $ perl -le 'print for glob "perles*.pod"'
    perles-01.pod
    perles-02.pod
    perles-03.pod
    perles-04.pod
    perles-05.pod

Dans les anciennes version de Perl, cet opérateur était d'ailleurs implémenté par un appel à csh. Depuis la version 5.6.0 de Perl, la fonctionnalité est fournie par le module File::Glob.

En contexte de liste, glob() renvoie la liste des fichiers correspondants. En contexte scalaire, glob() se comporte comme un itérateur et renvoit un nouveau nom de fichier jusqu'à épuisement de la liste (à ce moment-là, il renvoie undef). C'est très utile dans une boucle while, par exemple.

Notez que Perl, fidèle à l'esprit DWIM (Do What I Mean), fait ce qu'il faut dans le cas simple où vous écrivez while(glob"motif"), puisqu'il exécute en fait while (defined($_ = glob('motif'))). Non seulement il met à jour $_ (encourageant ainsi votre paresse), mais en plus il vous évitera des problèmes si un fichier nommé 0 existe dans votre répertoire courant.

Les caractères spéciaux reconnus par glob() sont :

Tous ces éléments se combinent, bien sûr ; un motif comme perles-{0,1}{*,4,6} produira la liste suivante : perles-01.pod, perles-02.pod, perles-03.pod, perles-04.pod, perles-05.pod, perles-04.pod, perles-06.pod, perles-14.pod, perles-16.pod. Le fichier perles-04.pod apparaît deux fois, car il est mentionné implicitement par * et explicitement par 4 dans l'alternative. Comme il n'existe aucun fichier correspondant à perles-1*.pod, glob() n'ajoute que les deux fichiers mentionné explicitement.

Le fait que glob() puisse contruire une liste à partir d'éléments explicitement donnés m'a été utile pour écrire un script .BAT dans le cadre de mon travail. Il fallait relancer un script toutes les 5 minutes, tous les jours et cette installation devait être faite automatiquement par mon script. La commande at de Windows n'étant pas aussi élaborée que cron, hélas. Au lieu d'écrire plusieurs boucles foreach imbriquées, je me suis contenté d'écrire le motif correspondant à ce que je voulais.

    C:\> perl -e '$"=",";print"at $_ every:@{[1..31]} %COMMANDE%\n" for glob qq<{@{["00".."23"]}}:{@{[map{sprintf"%02d",$_*5}0..12]}}>'
    at 00:00 every: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 %COMMANDE%
    at 00:05 every: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 %COMMANDE%
    at 00:10 every: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 %COMMANDE%
    at 00:15 every: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 %COMMANDE%
    at 00:20 every: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 %COMMANDE%
    ...

Je vous laisse décortiquer cet uniligne en guise d'exercice.

glob() est décrit en détail dans perlop(1), section I/O Operators.

(Philippe "BooK" Bruhat, Paris.pm - book@mongueurs.net)

À vous !

Envoyez vos perles à perles@mongueurs.net, elles seront peut-être publiées dans un prochain numéro de Linux Magazine.

[IE7, par Dean Edwards] [Validation du HTML] [Validation du CSS]