open()
ou avec des backquotes
plutôt que de récrire la fonction en Perl. Cependant il y a de solides
raisons qui font que cela pourait être sous-optimal. Tout d'abord, il y a la sempiternelle efficassité. L'invocation d'un sous process a un coût important. Bien sur à partir du moment où la taille des données à traiter augmente, ce surcoût diminue en proportion. Comme pour toute optimisation, vous devez expérimenter : essayer différentes solutions sur un échantillon de données réelles pour trouver l'approche la plus efficasse.
La portabilité est un argument plus parlant à l'encontre de
l'utilisation des outils que les constucteurs livrent avec leur
système. Si vous avez déjà eu à maintenir un logiciel sur plusieurs
plateformes, vous connaissez les difficultés qu'il y a à trouver
l'outil dont vous avez besoin. Où se trouve donc la commande
find
- /bin,
/usr/bin
ou d'un
autre coté obscure du système ? Accepte-elle les mêmes options sur
toutes les plateformes ? Multipliez ces questions par le nombre
d'utilitaires UNIX dont votre programme Perl peut avoir besoin et leur
réimplémentation vous apparaîtra soudain moins coûteuse. Si vous
maintenez la même version de Perl sur toutes vos plateformes vous
aurez une base de travail commune. Entre autres objectifs, cet article
explicite quelques méthodes simples pour émuler en Perl divers
utilitaires UNIX.
chown()
, chmod()
, mkdir()
, et
rmdir()
. On trouve également des fonctions link()
and symlink()
pour créer respectivement des liens durs
et des liens symboliques de même que unlink()
pour
effacer un fichier et même rename()
une fonction qui
émule partiellement mv
.
Les functions chown()
, chmod()
et
unlink()
opèrent sur une liste de fichiers et retournent
le nombre de succès. Il est cependant parfois nécessaire de
connaître la nature exacte des échecs. Dans ce cas, on pourra
utiliser la boucle suivante sur chaque élément de la liste :
for (@files) { chown(0644, $_) || warn "Can't change permissions on $_\n"; }La même méthode peut être utilisée pour les opérations ne travaillent pas directement sur des listes de fichiers.
Notez que si votre système ne supporte pas une des fonctions
précédantes, vous rencontrerez des erreurs plus ou moins bien
traitées. Par exemple, si les liens symboliques ne sont pas
supportés, alors l'appel système symlink
causera la mort
de votre programme avec une erreur fatale à l'exécution. Comme pour
toute fonction dont vous n'êtes pas certain, il est bon de l'enclore
dans un eval()
pour intercepter les éventuelles
erreurs :
eval "symlink($old, $new);"; warn "Symlink not supported\n" if ($@);Perl garantit que la variable
$@
est nulle si
eval()
réussit, c'est un test tout à fait fiable.
Parfois Perl invoque simplement la commande standard si elle n'est pas
disponible dans une bibliothèque du système. C'est le cas de
mkdir
. Il vaut alors mieux que vous invoquiez vous même
cette commande avec la liste de répertoires à créer plutôt que de
créer un sous process à chaque invocation de mkdir
dans
la boucle précédante.
split()
et substr()
émulent de
très près cut
. Perl dispose d'une fonction
sort
interne bien plus puissante que le sort
UNIX. Vous aurez cependant à définir vous même votre propre fonction
de tri. (Voyez pour cela le premier article
de cette série pour plus d'informations sur les tris en
Perl). Parfois vous aurez à changer légèrement votre tournure
d'esprit pour parvenir à ce que Perl fasse ce que vous souhaitez.
Par exemple, les programmeurs ont coutume d'utiliser basename
et
dirname
pour obtenir le nom du programme invoqué (pour construire les
messages d'erreurs) ainsi que le nom du répertoire où il a été chargé. Perl
mémorise le chemin complet du nom du script invoqué dans la variable
$0
. basename
et dirname
peuvent être émulées
avec les substitions d'expressions régulières suivantes :
($basename = $0) =~ s%.*/%%; ($dirname = $0) =~ s%/[^/]*$%%;La première substitution utilise la recherche gourmande des motifs pour consommer tous les caractères jusqu'à la dernière occurence de '/' dans le path. Si le nom du fichier vous intéresse également, vous pouvez utiliser l'unique instruction suivante :
($dirname, $basename) = $0 =~ /(.*)\/(.*)/;Ici aussi nous utilisons la recherche gourmande mais nous utilisons en plus la possibilité de retourner une liste de sous-expressions. L'instruction peut paraître étrange, mais l'ordre de précédence est correct.
open(FILE, "< myfile") || die "Can't open myfile\n"; while (<FILE>) { next if $seen{$_}++; ...do some processing here... } close(FILE);Notez que cette fonction peut consommer beaucoup de mémoire si le fichier est gros. En revanche, le tableau associatif
%seen
contient le nombre
d'occurrences de chaque ligne ce qui est pratique si vous tenez à émuler uniq
-c
. Vous pouvez aussi exécuter sort
si vous tenez absoluement à
trier la sortie.
open(FILE, "< myfile") || die "Can't open myfile\n"; @lines = <FILE>; close(FILE); @found = grep(/$pattern/, @lines);Ceci peut cependant être gourmand en mémoire pour des gros fichiers. Dans ce cas vous pouvez travailler séquentiellement :
open(FILE, "< myfile") || die "Can't openmyfile\n"; while (<FILE>) { next unless (/$pattern/); ...process here... } close(FILE);Si vous avez besoin de la liste des lignes contenant le motif, il vous suffit de les mémoriser par
push
dans le traitement de la
boucle. Vous n'aurez pas à garder en mémoire l'intégralité du fichier.
Un package s'utilise en le requérant puis en appelant les fonctions qu'il
contient comme vous le feriez avec n'importe laquelle de vos propres
fonctions. Par exemple le package ctime.pl
offre des fonctionnalités
similaires à celles de la commande UNIX date
:
require "ctime.pl"; $date_str = &ctime(localtime);Bien sur ceci ne procure pas les capacités de formatage de
date
. Mais
vous pouvez toujours utiliser printf
pour les émuler.
Dans la catégorie "facile à utiliser" on trouvera également getcwd.pl
et fastgetcwd.pl
pour vous aider à retrouver votre chemin dans
l'arbre des répertroires. La fonction définie dans fastgetcwd.pl
est
plus efficace parce qu`elle utilise chdir()
pour remonter le chemin
jusqu'à la racine, par contre vous ne serez pas en mesure de revenir à votre point
de départ. Pour ceux qui en C-shell aiment la variable $PWD
, il y a
la bibliothèque pwd.pl
. Insérez simplement
&initpwd()
après avoir requis la bibliothèque et utilisez la
conction &chdir()
définie dans le package au lieu de la fonction
interne à Perl. La fonction chdir()
maintiendra en permanence à jour
la variable d'environnement $PWD
.
L'entrée la plus utile de la bibliothèque Perl est sans conteste
find.pl
. La réunion de toutes les options du find
des
différents UNIX est énorme, par contre leur intersection est souvent
minimale. Essayer d'écrire une application portable utilisant find
relève souvent de la recherche opérationnelle. find.pl
a été écrite
pour les besoins du programe find2perl
livré avec la distribution de
Perl mais vous pouvez l'utiliser pour vos propres programmes.
La fonction &find()
définie dans la bibliothèque Perl accepte une
liste de fichiers et de répertoires en argument et parcours cette liste comme le
fait la commande find
UNIX. Pour chaque item rencontré lors du
parcours, &find()
invoque la routine &wanted
définie par l'utilisateur. Elle ne reçoit pas d'argument mais la variable
$dir
contient le nom du répertoire courant, $_
celui de
l'item courant et $name
le path complet "$dir/$_
". Si
$_
est un répertoire, la fonction &wanted
peut
définir à vrai
la variable $prune
pour empêcher
&find
de continuer la recursion dans $_
.
À part ceci, l'exécution de &wanted
reste entièrement sous le
contrôle de l'utilisateur. L'utilisation judicieuse de stat()
ou de
lstat()
et des opérateurs de test de Perl sur les fichiers permettent
d'émuler la plupart des options de find
proposées par les différents
UNIX. ( N'oubliez pas que la variable $_
contient le résultat de
la dernière invocation de stat()
ou lstat()
, qu'elle ait
été invoquée directement ou par l'intermédiaire d'un opérateur de test sur les
fichiers). Bien sur Perl est un langage plus riche et plus puissant que la
syntaxe de la ligne de commande de find
. Ainsi des effets de bords
très puissants peuvent être obtenus. Pour en avoir une meilleur idée, faites
tourner find2perl
sur vos invocations favorites de find
et étudiez attentivement sa sortie. (Il y a certainement une douzaine de bons
candidats simplement dans /usr/spool/cron/crontabs/*
).
Il y a une foule d'autres outils disponibles dans la bibliothèque Perl. Dans
look.pl
, vous trouvez une recherche dans un dictionnaire qui émule la
commande UNIX look
. Il y a même syslog.pl
une interface
à syslog
pour le cas où vous n'aimeriez pas passer votre temps à
appeler logger
. D'autres bibliothèques sont postées dans
comp.lang.perl et archivées sur coombs chaque jour.
s2p
et
a2p
livrés avec la distribution Perl). Parfois cependant vous aurez
vraiment besoin d'appeler des outils au dehors de Perl. Par exemple je cherche
toujours quelque chose de mieux que :
chop($hostname = `/bin/hostname`);La fois prochaine nous parlerons des stratégies à mettre en oeuvre pour écrire des scripts Perl portables sur l'affolante variété d'implémentations d'UNIX subtilement différentes les unes des autres dans leurs chemins et dans le comportement de leurs commandes. Le titre provisoire pourrait être "Ce que j'aime avec les standards c'est que vous avez toujours le choix entre plusieurs".
Traduction de ;login: Vol. 18 No. 6, décembre 1993.
Dernière édition: 21 décembre 1997 phb Original 11/22/96pc |
|