Article publié dans Linux Magazine 52, juillet/août 2003.
Copyright © 2003 - Philippe Bruhat.
Perl a la réputation d'être un langage illisible (write-only, disent ses détracteurs anglophones) dont le code ressemble à du "bruit de modem" (ou line-noise), une suite incohérente de caractères. Ceci est dû à l'usage intensif de caractères non alphanumériques, en particulier pour les variables.
Cet article présente les principales de ces variables, et va vous apprendre à les connaître et à les utiliser.
Dans le cas des variables de Perl, le sigil est un caractère non
alphanumérique qui détermine le type d'une variable en précédant son
nom. $x
est un scalaire, @x
un tableau, %x
un hashage. Dans
le cas des variables spéciales, le nom lui-même est souvent aussi un
caractère non alphanumérique.
Les variables qui suivent sont indispensables ! Il vous faudra maîtriser au moins celles-ci avant de devenir un véritable Perl Adept.
$_
$_
est la variable par défaut (ou la variable contenant la "valeur
courante"). Elle fait partie des éléments les plus intéressants de Perl,
en apportant à la fois concision et lisibilité à vos programmes.
Perl a été créé par un linguiste et utilise certains des mécanismes
des langage naturels dont le pronom et l'ellipse. Justement,
$_
fonctionne comme un pronom. Prenons par exemple cette conversation
(fictive) entre deux utilisateurs de Perl :
Larry Wall est l'auteur de Perl.
Wouahou, il a fait tout ça tout seul ?
Non, des centaines de programmeurs l'ont aidé depuis 15 ans. D'ailleurs il ne programme plus directement Perl : il se consacre à la création de la prochaine version de Perl, Perl 6.
Dans cette petite conversation, "Larry Wall" est mentionné une seule fois, mais est le sujet ou l'objet de cinq phrases au total. Les interlocuteurs n'ont pas besoin de rappeler à chaque fois que c'est de Larry qu'on parle. Il (Larry) est le sujet implicite de ces différentes phrases.
$_
fonctionne exactement de la même façon : c'est l'objet implicite
sur lequel s'appliquent certaines fonctions ou opérations quand aucune
variable n'est mentionnée.
De nombreuses fonctions unaires et certaines fonctions de liste utilisent
$_
. Plus précisément, toutes les fonctions qui utilisent $_
quand il n'est
pas précisé d'argument (ou qu'un ou plusieurs d'entre eux sont absents)
sont : -X
, abs
, chomp
, chop
, chr
, cos
, defined
,
eval
, exp
, glob
, hex
, int
, lc
, lcfirst
, length
,
log
, lstat
, oct
, ord
, pos
, print
, quotemeta
,
readlink
, ref
, require
, rmdir
, sin
, split
, sqrt
,
stat
, study
, uc
, ucfirst
, unlink
.
Dans la liste qui précède, -X
représente l'ensemble des fonctions de
test sur les fichiers (c'est-à-dire -r
, -w
-x
, -o
, -R
,
-W
, -X
, -O
, -e
, -z
, -s
, -f
, -d
, -l
, -p
,
-S
, -b
, -c
, -u
, -g
, -k
, -T
, -B
, -M
, -A
et -C
). La seule exception est -t
, qui agit sur STDIN
quand
aucun argument n'est fourni.
Les opérations de correspondance de motif m//
, s///
et tr///
(ou y///
) s'appliquent à $_
quand elles sont utilisées sans l'opérateur
=~
.
On peut donc utiliser la "valeur courante" sans y faire référence. Mais
cette construction ne serait pas si intéressante si $_
n'était pas mis à
jour de façon tout aussi implicite par certaines constructions Perl, à
savoir foreach
et while
.
Ainsi, $_
est l'itérateur par défaut de foreach
quand aucune
variable n'est fournie explicitement. Ceci se combine admirablement
avec l'utilisation de foreach
comme modificateur d'instruction (les
modificateurs sont if
, unless
, while
, until
, foreach
,
placés après une instruction simple).
# n'affiche que certains trucs /regex/ && print for @trucs; # autre formulation print grep { /regex/ } @trucs;
À comparer par exemple avec :
# n'affiche que certains trucs (les mêmes qu'avant) foreach my $truc (@trucs) { print $truc if $truc =~ /regex/; }
Lorsque le résultat d'un <FH>
est utilisé comme seul critère de
test d'un while
, il est placé dans $_
. Ceci n'est valable que dans un
while
. De plus, while(<FH>)
est "magique", et correspond en
fait à une opération un peu plus complexe qu'un simple test.
while (<FH>) { # la ligne courante est stockée dans $_ ... }
est équivalent à :
while ( defined( my $ligne = <FH> ) ) { # la ligne courante est stockée dans $ligne ... }
Note : Le defined
n'est pas nécessaire ici, grâce à la magie de
while() . En revanche, dans un test plus complexe que celui-ci (boucler
tant qu'il y a quelque chose à lire), il est préférable d'utiliser
une affectation avec un test de définition, sans quoi vous prendriez
le risque d'être confrontés à un subtil bug.
Mais imaginons le script suivant, qui affiche les premières lignes d'un fichier, et servira à vous présenter ce fameux bug :
open F, "< $fichier" or die "Impossible d'ouvrir $fichier: $!"; while ( my $ligne = <F> and $. <= 10 ) { print $ligne; }
$.
est le numéro de la ligne courante, comme vous allez le lire plus loin.
Ce programme n'affiche donc que les 10 premières lignes du fichier.
Si jamais le fichier se termine par un 0
sans saut de ligne final
(et fait moins de 10 lignes), la dernière ligne ne sera pas affichée.
Pourquoi ?
Dans Linux Magazine 41, Sylvain vous a présenté les valeurs scalaires
que Perl considère comme fausses. Pour mémoire, il s'agit de : ""
(la chaîne vide), 0
(la valeur numérique 0), "0"
(la chaîne
contenant comme seul caractère 0
(code ASCII 48)) et undef
.
Ainsi, la dernière ligne fera renvoyer une valeur fausse ("0"
)
à l'affectation, ce qui va faire échouer le test et terminer
(prématurément) la boucle.
Et pourtant, si vous écrivez while(<F>)
ou
while( my $ligne = <F> )
, la dernière ligne ne sera
pas ignorée. C'est parce que le compilateur Perl est capable
de reconnaître ces cas simples, et va traduire le code qui sera
exécuté en while( defined( my $ligne = <F> ) )
.
Comme vous le verrez au cours de vos expérimentations avec Perl, Perl se met en quatre pour faire "ce à quoi vous pensez". C'est ce que dans la communauté Perl on appelle le principe DWIM (Do What I Mean).
Tout ce qui précède apparaît admirablement dans l'exemple qui suit :
# imprime la liste des utilisateurs open F, "< /etc/passwd" or die "Impossible d'ouvrir /etc/passwd : $!"; while (<F>) { next if /^\s*(?:#|$)/; # ignore les commentaires et lignes blanches s/:.*//; print; } close F;
qui est tout de même plus lisible (moins de variables intermédiaires) et plus rapide à taper (moins de caractères) que :
# imprime la liste des utilisateurs open F, "< /etc/passwd" or die "Impossible d'ouvrir /etc/passwd : $!"; while (defined my $ligne = <F>) { next if $ligne =~ /^\s*(?:#|$)/; $ligne =~ s/:.*//; print $ligne; } close F;
Note : Les super-pouvoirs que vous avez acquis depuis votre exposition aux radiations de mon article précédent (Linux Magazine 50), vous permettent de réduire instantanément ce programme au one-liner suivant :
$ perl -lpe 'next if/^\s*(?:#|$)/;s/:.*//' /etc/passwd
Pour terminer, $_
est l'itérateur implicite des fonctions grep
et map
,
ce qui se combine parfaitement avec les cas précédents :
# récupère la liste des sous-répertoires de $dir dans @subdirs opendir( DIR, $dir ) or die "Impossible d'ouvrir $dir: $!"; my @files = readdir(DIR); # Note : @files contient des fichiers relatifs à $dir my @abs = map { "$dir/$_" } @files; my @subdirs = grep { -d } @abs; closedir(DIR);
On peut évidemment faire plus clair, et plus court :
# récupère la liste des sous-répertoires de $dir dans @subdirs opendir( DIR, $dir ) or die "Impossible d'ouvrir $dir: $!"; my @subdirs = grep { -d } map { "$dir/$_" } readdir(DIR); closedir(DIR);
@ARGV
@ARGV
contient la liste des paramètres passés sur la ligne de commande au
script en cours d'exécution. Le nom @ARGV
tire ses origines du tableau
argv
utilisé en C, à ceci près que $ARGV[0]
n'est pas le nom du
script en cours d'exécution, mais le premier argument passé à votre
script.
Dans la partie principale du programme (c'est-à-dire en dehors des définitions
de sous-programmes par sub {}
), pop
et shift
agissent sur @ARGV
.
@ARGV
s'utilise le plus souvent comme $_
, c'est-à-dire de manière
implicite. Ainsi, dans l'exemple de code qui suit, beaucoup de choses
sont faites de façon implicite :
#!/usr/bin/perl while (<>) { # traitement complexe sur $_ }
En fait, @ARGV
est principalement lié à open() . Mais, me direz-vous,
on n'a même pas utilisé open() dans le code qui précède ? En effet,
l'utilisation de open() fait justement partie des choses implicites dont
je viens de parler...
Quand vous utilisez <>
pour traiter ligne par ligne les fichiers
passés en paramètre à votre script, Perl fait un open() implicite de
chaque fichier listé dans @ARGV
(et pousse le dévouement jusqu'à
émettre un avertissement si l'un des fichiers n'existe pas).
L'exemple de code ci-dessus réalise donc un "traitement complexe" sur
chaque ligne des fichiers listés dans @ARGV
(c'est-à-dire sur chacun
des fichiers passés en paramètre à votre script). Et ceci avec une
construction somme toute assez simple.
De plus, si @ARGV
est vide lors de la première itération de la boucle,
Perl prétendra avoir ouvert le fichier -
(tiret), c'est-à-dire l'entrée
standard (voir également $ARGV
, ci-après). On retrouve ainsi le
fonctionnement en "filtre" qui est l'une des base de la philosophie Unix.
Vous pouvez parfaitement modifier le contenu de @ARGV
avant de commencer
une boucle utilisant <>
. Ceci permet en particulier de
traiter les options de ligne de commande (par exemple avec les modules
Getopt::
qui vous ont été présentés par Jérôme Quelin dans Linux
Magazine 49) qui précèdent les noms des fichiers à traiter.
Une autre possibilité est de construire dynamiquement la liste des
fichiers à traiter à partir des options. Par exemple, si aucun fichier
n'a été précisé, plutôt que de traiter STDIN
, vous pourriez traiter
tous les fichiers du répertoire courant :
@ARGV = glob("*") unless @ARGV;
Ou même seulement les fichiers (et pas les répertoires) :
@ARGV = grep { -f } @ARGV;
Bien sûr, si vous utilisez l'option -n ou -p dans la ligne
shebang, n'oubliez pas de faire vos modifications de @ARGV
dans
un bloc BEGIN{}
(car la boucle while(<>)
est elle-même
implicite).
Pour résumer, le contenu de @ARGV
est ouvert automatiquement quand
vous utilisez une boucle while(<>)
dans le cadre d'un
fonctionnement de type filtre. Bien sûr, ces fichiers sont ouverts
avec le open() de Perl. Du coup, vous avez accès à toute la magie
dont open() est capable. C'est-à-dire que si par exemple vous modifiez
le contenu de @ARGV de façon à ce que les noms de fichiers soient en
fait des chaînes de type "commande argument |"
, les lignes seront
alors lues depuis un autre processus forké pour l'occasion.
Voici deux exemples de tels traitements pour rendre cela un peu plus clair :
@ARGV = map { /^\.(gz|Z)$/ ? "gzip -dc $_ |" : $_ } @ARGV;
Si les fichiers passés en paramètre sont compressés, Perl les décompressera (avec gzip) pour pouvoir les traiter.
Encore plus fort, et sur le même principe, voici comment traiter des fichiers téléchargés directement sur le net :
@ARGV = map { m!^\w+://! ? "lynx -dump $_ |" : $_ } @ARGV;
Ou, si voulez traiter les sources HTML :
@ARGV = map { m!^\w+://! ? "lynx -source $_ |" : $_ } @ARGV;
La magie de open() a tout de même un inconvénient : vous ne pourrez pas traiter un fichier nommé - (tiret). Mais il y a plein d'astuces pour s'en tirer, comme par exemple l'appeler en tant que ./-.
@_
@_
contient les arguments passés au sous-programme en cours (sub
).
À l'intérieur d'une routine, pop
et shift
agissent sur @_
.
C'est ainsi que l'on voit souvent des routines commencer comme ceci :
sub rot13 { my $string = shift; $string =~ y/A-Za-Z/N-ZA-Mn-za-m/; return $string; }
Et pour les méthodes des modules objets (une référence à l'instance en cours de l'objet étant toujours passée en premier paramètre à la méthode):
# un accesseur pour l'attribut color d'un objet sub color { my $self = shift; my $old = $self->{color}; $self->{color} = shift if @_; return $old; }
Voire :
sub print_color { print shift->{color} }
Les variables qui suivent concernent les entrées/sorties de Perl, elle permettent de changer l'idée que Perl se fait de la notion de ligne, de modifier facilement l'affichage de champs en sortie, etc.
$/
est le séparateur d'enregistrements en entrée. Par défaut, $/
est
un saut de ligne. Cette variable modifie l'idée que Perl se fait de
ce qu'est une ligne, en particulier avec l'opérateur <>
(parfois appelé readline() ).
La notion de ligne dépend du système d'exploitation utilisé. Plus
précisément, sous Unix les lignes seront délimitées par le caractère
\012
, tandis que sous DOS/Windows ce sera par \015\012
et que sous
Mac on utilisera \015
. Heureusement, Perl initialise $/
directement
à la valeur habituelle sur le système utilisé (ceci pourra vous jouer
des tours si vous traitez un fichier DOS sous Unix et réciproquement).
Il est important de noter que $/
peut être une chaîne de plusieurs
caractères. Ceci permet des découpages un peu plus élaborés du fichier
en entrée :
my $fortune; local $/ = "\n%\n"; while(<>) { $fortune = $_ if rand $.; } print $fortune;
Cet algorithme, qui permet de sélectionner une entrée de façon équiprobable (au moins au sens de rand() ), est expliqué à la recette 8.6 (Picking a Random Line from a File) du Perl Cookbook (Perl en action dans la version française), publié chez O'Reilly.
Notez l'utilisation de local() pour modifier $/
. Quand on modifie les
variables spéciales de Perl, surtout celles qui concernent les
entrées/sorties, local() permet de limiter la portée de ces modifications
au bloc (dynamique) qui entoure la modification. On peut ainsi s'assurer
que les modifications n'altéreront pas le fonctionnement habituel de
Perl à d'autres endroits du code (par exemple dans des routines définies
par d'autres modules utilisés).
Il existe deux valeurs de $/
pour lesquelles <>
a un
comportement un peu particulier.
Si $/
vaut undef
, readline() lira le fichier jusqu'à la fin en une
seule fois. C'est ce qu'on appelle le slurp mode, ou mode aspiration.
Si $/
vaut ""
, on passe en mode paragraphe, c'est-à-dire que
readline() renverra le fichier paragraphe par paragraphe (deux
paragraphes étant séparés par des lignes vides). Si vous affectez
"\n\n"
à $/, vous n'obtiendrez pas tout à fait le même résultat,
puisque Perl considérera que les caractères qui suivent $/
font partie
du prochain paragraphe, tandis qu'avec ""
plusieurs lignes vides
consécutives sont traitées comme une seule séparation de paragraphe
(et les paragraphes sont alors de vrais paragraphes).
Attention, $/
n'est pas utilisé comme une expression régulière.
Il serait d'ailleurs particulièrement difficile d'écrire un module
qui permette d'utiliser un équivalent de $/
qui soit une expression
régulière. À ce sujet, voir le blog de Nat Torkington (l'un des auteurs
du Perl Cookbook) qui mentionne le problème dans $RS as Regex,
http://use.perl.org/~gnat/journal/11599.
Notez que chomp() se sert de $/
pour déterminer quelle suite de caractères
supprimer à la fin d'une chaîne. Ceci permet d'éliminer un bug subtil
(et non portable) que vous pourriez faire si vous utilisiez chop().
En effet, chop() supprime le dernier caractère d'une chaîne, quel
qu'il soit, tandis que chomp() supprime la sous-chaîne finale d'une
chaîne seulement si celle-ci est égale à $/
. chop() est assez rarement
utilisé.
$.
$.
est le numéro de ligne (plus précisément le numéro d'enregistrement)
courant du dernier fichier à partir duquel vous venez de lire quelque
chose. Il peut effectivement être différent du véritable numéro de ligne
si vous avez modifié l'idée que Perl se fait des lignes à l'aide de $/
.
Voici un petit uniligne pour numéroter les lignes d'un fichier (éh oui, il est légèrement différent de celui de l'article précédent ; s'il fallait prouver qu'il y a plus d'une manière de faire, c'est fait).
$ perl -ne 'printf"%6d $_",$.'
$.
est réinitialisé lors d'un close() explicite sur un handle de fichier.
Pour savoir comment réinitialiser $.
entre deux fichiers passés sur @ARGV
(avec un close(ARGV)
), reportez-vous à la documentation de eof().
@F
Nous avons déjà vu @F
dans l'article sur les programmes d'une
ligne. C'est le tableau qui reçoit les éléments autosplités quand
l'option -a a été utilisée sur la ligne de commande.
@F
ne sert que pour les unilignes.
$ARGV
$ARGV
contient simplement le nom du fichier en cours de lecture lorsqu'on
utilise la construction <>
. Lorsque Perl a ouvert l'entrée
standard (car @ARGV
était vide, voir plus haut), $ARGV
contient
évidemment -
.
Pour être complet, précisons que le handle de fichier associé à $ARGV
et <>
est ARGV
. Il est en fait rarement utilisé, sauf avec
eof() (vous pouvez à nouveau consulter la documentation de cette
fonction pour plus de détails).
$\
$\
est le séparateur d'enregistrement pour la fonction print() .
Par défaut, $\
est vide, c'est-à-dire qu'habituellement, print() se
contente d'imprimer la liste de ses arguments, sans rien de plus.
$ perl -e 'print 1..5' 12345
Si vous voulez ajouter une chaîne à la fin de chaque appel à print() ,
vous pouvez utiliser $\
en conséquence. On peut dire que $\
est plus
ou moins le contraire de $/
.
$ perl -e '$\ = ","; print 1..5' 12345,
Attention, $\
n'a aucun effet sur printf() .
$,
$,
est le séparateur de champs pour la fonction print() .
Par défaut, $,
est vide, c'est-à-dire qu'habituellement, print() se
contente d'imprimer la liste de ses arguments. Même exemple que
précédemment :
$ perl -e 'print 1..5' 12345
Si vous voulez afficher quelque chose entre deux champs, vous pouvez
modifier $,
en conséquence.
$ perl -e '$, = ","; print 1..5' 1,2,3,4,5
$"
$"
fonctionne comme $,
sauf qu'elle s'applique aux tableaux et tranches
qui sont interpolés dans une chaîne entre guillemets (ou les chaînes
qui sont interprétées de la même manière).
Par défaut, $"
est un espace :
$ perl -e '@a = ( 1 .. 5 ); print "@a"' 1 2 3 4 5 $ perl -e '@a = ( 1 .. 5 ); $" = ","; print "@a"' 1,2,3,4,5
Notez bien que c'est différent de l'affichage du même tableau sans interpolation :
$ perl -e '@a = ( 1 .. 5 ); print @a' 12345
$|
La principale utilisation de cette variable se résume à la ligne suivante :
$| = 1;
Si $|
est vraie, le mode d'écriture systématique des tampons (autoflush)
est activé sur le handle de fichier courant (en général STDOUT
).
Si vous désirez activer l'autoflush sur un handle de fichier autre que
STDOUT
, vous pouvez utiliser la construction suivante, assez
idiomatique, mais peu lisible :
select( ( select(FH), $| = 1 )[0] );
select()
permet en effet de sélectionner le handle de fichier courant,
qui sera celui sur lequel la modification faite à $|
aura un effet.
Il retourne l'ancien handle de fichier, qui est ici récupéré par le
[0]
appliqué à la liste de deux éléments.
De nos jours, on utilise plutôt IO::File, avec la méthode autoflush() (héritée de IO::Handle). C'est tout de même plus lisible :
use IO::File; $fh = new IO::File; $fh->open("> fichier") or die "Erreur: $!"; $fh->autoflush(1);
Ou pour faire le strict équivalent de $| = 1
(sur STDOUT
par défaut) :
use IO::File; autoflush STDOUT 1;
$|
est généralement utilisé dans les scripts vite écrits, vite jetés,
ou alors par les golfeurs Perl (mais il vaut mieux pour votre santé mentale
que vous ignoriez ce qu'ils font avec).
Bien programmer, c'est aussi se préparer à l'échec : que se passe-t-il
si le fichier que vous voulez ouvrir n'existe pas ? Et si la chaîne
que vous eval()
uez contient une erreur de syntaxe ?
La gestion des erreurs en Perl est un petit peu compliquée par le fait
qu'il existe plusieurs variables dédiées aux erreurs.
Il y a au total quatre variables d'erreur en Perl : $@
, $!
, $^E
et $?
. Elles correspondent respectivement aux erreurs renvoyées par
l'interprêteur Perl, la librairie C (errno
), le système d'exploitation
et un programme externe.
On peut expliquer cette abondance de deux manières :
Perl fait le lien entre des environnements hétérogènes : le compilateur Perl, le système d'exploitation, les spécifités du système d'exploitation et les processus ;
avec un peu plus de mauvaise foi, on est tellement conscient de l'importance du traitement d'erreur qu'il n'y a pas moins de quatre variables pour cela !
En fait, les variables d'erreur les plus couramment utilisées sont $@
et $!
.
$@
$@
contient le message d'erreur du dernier eval() qui a été exécuté.
Si $@
est vide, cela signifie que le dernier eval() a été évalué et
exécuté correctement.
Seuls les messages des erreurs fatales sont capturés par $@
. Les
avertissements sont traités normalement (c'est-à-dire envoyés sur
STDERR
, en général).
eval { }
(le eval
d'un bloc, à ne pas confondre avec le eval
d'une chaîne) utilisé avec $@
et die() permet de mettre en œuvre un
mécanisme d'exception (comme en Java). Voici un exemple :
eval { open F, $file or die "IO: $!"; while (<F>) { # assertion die "ASSERT: $file contient une ligne de '#'" if /^#+$/; # faire quelque chose d'utile ici } close F or die "IO: $!"; }; # gestion des exceptions levées if ( $@ =~ /^IO:/ ) { # erreur d'entrée sortie } elsif ( $@ =~ /^ASSERT:/ ) { # une assertion a échoué }
Si on sort du bloc normalement, $@
est vide, sinon $@
contient le
message passé à die() .
$!
$!
contient l'erreur système courante (le fameux errno
).
Cette variable est très magique : utilisée en contexte numérique,
elle renverra la valeur numérique de errno
(qui dépend du système),
alors qu'utilisée comme une chaîne de caractères, elle contiendra le
message d'erreur.
De plus, si vous affectez une valeur numérique à $!
, le message d'erreur
dans $!
en tant que chaîne sera le bon. Et si le programme meurt (avec
die() ), le statut de sortie du programme aura la valeur correspondante.
$ perl -e '$! = 13; die' ; echo $? Died at -e line 1. 13
Voici un exemple de script qui affiche tous les messages d'erreur du système, avec la traduction numéro/message (et en plus, vous aurez des messages différents selon la locale qui est définie) :
$ LC_ALL=C perl -e '$! = $_, print 0 + $!, " $!\n" for 1 .. 128' 1 Operation not permitted 2 No such file or directory 3 No such process 4 Interrupted system call ...
Ou en français :
$ LC_ALL=french perl -e '$! = $_, print 0 + $!, " $!\n" for 1 .. 128' 1 Opération non permise 2 Aucun fichier ou répertoire de ce type 3 Aucun processus de ce type 4 Appel système interrompu ...
$^E
Cette variable fournit des informations d'erreur spécifiques au système
d'exploitation. Si $^E
n'est pas supportée par le système d'exploitation,
alors elle sera égale à $!
.
En fait, $^E
n'est différente de $!
que sous VMS, OS/2, MacPerl et
Win32. Ce qui la rend de peu d'intérêt pour un lecteur de Linux Magazine.
$?
$?
contient le statut de sortie renvoyé par la dernière commande qx()
(ou ``
), le dernier appel réussi à wait() ou waitpid() ou la
dernière fermeture d'un tube ; en bref, le résultat du dernier
sous-processus exécuté. Ce n'est pas pour rien qu'elle porte le même
nom que la variable équivalente du shell.
En fait, c'est un statut sur 16 bits. La valeur de sortie du
sous-processus est donnée par $? << 8
. Le reste du mot donne
d'autres informations, soit en résumé :
$exit_value = $? >> 8; # valeur de sortie $signal_num = $? & 127; # numéro du signal qui a tué le processus $dumped_core = $? & 128; # vrai si c'était un core dump
Pour récupérer la bonne valeur de $?
quand vous avez ouvert un tube
sur un sous-processus (avec un open() sur "commande|"
, par exemple),
il faut impérativement faire un close() du handle de fichier.
Un certain nombre de variables permettent à vos scripts d'interagir avec le système (d'exploitation).
$0
$0
contient le nom du programme en train de s'exécuter.
Si le programme en cours d'exécution est donné avec l'option -e
de Perl, $0
contiendra simplement la chaîne "-e"
. Sinon, $0
contiendra le nom du script.
Sur certains systèmes d'exploitation, modifier $0
permet de modifier
le nom du programme tel qu'affiché par ps, ce qui permet de faire
comme sendmail et d'indiquer par exemple l'état du programme.
$$
Le numéro de process id (ou PID) du processus Perl en train de s'exécuter.
%ENV
Ce hachage contient l'ensemble des variables d'environnement définies lorsque le script a été lancé.
Bien évidemment, si vous créez ou modifiez une variable d'environnement
en affectant une valeur à l'une des clés de %ENV
, cela modifiera
l'environnement des processus lancés après ladite modification par
system(), qx() ou les formes de open() qui créent un sous-processus.
@INC
@INC
contient la liste des répertoires où Perl va aller chercher les
modules ou fichiers nécessaires pour exécuter une instruction do
,
require
ou use
.
Le tableau se comporte comme la variable d'environnement PATH
de
votre shell. Perl va successivement chercher le module demandé dans
les différents répertoires.
Les éléments contenus par défaut dans @INC
sont définis à la
compilation de Perl. Il est heureusement possible d'ajouter (ou
de supprimer) des répertoires à cette liste lors de l'exécution
d'un script.
Il existe trois manières de modifier le contenu de @INC
:
les variables d'environnement, l'option -I de perl
et
la pragma lib
. Vous pouvez également agir directement sur @INC
,
mais c'est en général réservé à ceux qui savent vraiment ce qu'ils font.
Deux variables d'environnement permettent d'ajouter des éléments à
@INC
: PERLLIB
et PERL5LIB
, qui contiennent une liste de
répertoires séparés par des deux-points (:
). Si PERL5LIB
est utilisé,
alors PERLLIB
est ignoré. Attention, quand Perl fonctionne en mode
taint checking, ces deux variables d'environnement sont ignorées. Dans
ce cas, il faut impérativement utiliser l'une des deux autres méthodes.
L'option -I permet d'ajouter une liste de répertoires (ici aussi
séparés par des deux-points) à @INC
. Cette option peut être utilisée
plusieurs fois.
Enfin, la pragma use lib
, suivie d'une liste de répertoires permet
également de les ajouter à @INC
. La documentation du module lib
explique également les détails de no lib
, qui permet de supprimer
des répertoires de @INC
(ce qui est rarement nécessaire).
Pour voir l'effet de chacune de ces directives sur le contenu de @INC
,
et l'ordre dans lequel elles sont prises en compte, utilisons le one-liner
suivant (ici sur une distribution Debian) :
$ PERL5LIB=env1:env2 perl -Iopt1:opt2 -Iopt3 -le 'use lib qw(lib1 lib2); use lib "lib3"; print for @INC' lib3 lib1 lib2 opt1 opt2 opt3 env1 env2 /usr/local/lib/perl/5.6.1 /usr/local/share/perl/5.6.1 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.6.1 /usr/share/perl/5.6.1 /usr/local/lib/site_perl .
Personnellement, dans les cas de dépendance envers des modules qui ne
sont pas installés dans les répertoires standards (par exemple modules
personnels sur une machine où je n'ai pas les droits d'administration
nécessaires pour l'installation de modules Perl), je préfère l'utilisation
de la variable d'environnement PERL5LIB
ou de l'option -I, car
cela n'impose pas de modification du script exécuté.
D'autres conditions d'utilisations justifient d'autres choix.
$^O
$^O
contient le nom du système d'exploitation sur lequel la version de Perl
en train de tourner a été compilée. C'est la même valeur que celle qu'on
trouve dans $Config{'osname'} (voir la page de manuel Config(3pm) pour
plus de détails).
Si vous réalisez du code qui a un comportement différent selon le système d'exploitation (cela peut arriver), vous pouvez être amenés à écrire du code comme celui-ci :
if ( $^O eq 'MSWin32' ) { # code spécifique Windows }
Voir perlport(1), section PLATFORMS pour avoir la liste des systèmes
supportés, et les valeurs de $^O
correspondantes.
$^X
$^X
contient le nom sous lequel le binaire de perl a lui-même été exécuté,
tel qu'obtenu par le argv[0]
de C. Il n'y a aucune garantie quant à
ce qu'elle contient, ce peut être ou non un chemin complet, par exemple.
$^W
$^W
contient la valeur courante de l'option d'avertissement de Perl.
$^W
est initialement vraie si -w a été utilisé.
Avant Perl 5.6.0, on utilisait généralement $^W
pour supprimer les
avertissements dans un bloc de code, comme ceci :
{ local $^W = 0; # désactive temporairement les avertissements $A = $B + $C; # ces variables sont potentiellement indéfinies }
À partir de Perl version 5.6.0, plutôt que de modifier $^W
, on peut utiliser
la pragma warning
. Celle-ci a l'avantage d'être à portée lexicale
(voir la page de manuel perllexwarn(1) pour plus de détails).
{ no warnings; # désactive temporairement les avertissements $A = $B + $C; # ces variables sont potentiellement indéfinies }
Notez également que vous ne pouvez pas utiliser my() sur $^W
, mais
seulement local() comme sur toutes les variables spéciales de Perl.
%SIG
Le modèle de gestion des signaux de Perl est assez simple : le hachage
%SIG
, indexé par les noms des signaux, contient des noms ou des
références (de code) de handlers pour ces signaux.
Tous les détails de la gestion des signaux en Perl sont expliqués dans la page de manuel perlipc(1). Nous ne nous étendrons donc pas dessus, sinon pour donner deux courts exemples de code. Un dernier rappel, vous êtes censés en faire le moins possible dans un handler de signal, car quasiment quoi que vous puissiez faire de plus compliqué que modifier une variable globale risque de provoquer un core dump.
Nous avons reçu un signal mortel :
$SIG{KILL} = $SIG{INT} = sub { $killed++ }; while ( not $killed ) { # un traitement très répétitif ... } # un peu de nettoyage post-traitement ...
Une manière de gérer des délais de garde (timeout) quand on dispose
du signal ALRM
:
eval { local $SIG{ALRM} = sub { die "TIMEOUT" }; alarm 10; # on se donne 10 secondes pour flock( FH, 2 ); # l'opération qui est bloquante alarm 0; # annulation du compteur }; if ( $@ and $@ !~ /TIMEOUT/ ) { # traitement d'erreur ... }
La page de manuel perlipc(1) est très complète sur le sujet des signaux, consultez-la si vous ne maîtrisez pas le sujet.
Si vous préférez utiliser des noms plus faciles à retenir (ou des noms
similaires à ceux utilisés par awk) pour ces variables prédéfinies,
vous pouvez toujours utiliser le module English
. Celui-ci crée des
alias en toutes lettres pour les variables de ponctuation.
Certaines variables ont même plusieurs noms, plus ou moins longs, le second venant en général du nom de la variable équivalente de awk.
Attention, l'utilisation du module English
avait un effet négatif sur
les performances, en particulier au niveau des expressions régulières.
Cependant, depuis la version 5.8.0 de Perl, on peut utiliser English
sans dégrader les performances. Dans ce cas, il faut taper :
use English qw(-no_match_vars);
Pour les variables que nous avons vues aujourd'hui les noms longs sont :
$_ $ARG @ARGV @_ $/ $INPUT_RECORD_SEPARATOR $RS $. $INPUT_LINE_NUMBER $NR $\ $OUTPUT_RECORD_SEPARATOR $ORS $, $OUTPUT_FIELD_SEPARATOR $OFS $" $LIST_SEPARATOR $| $OUTPUT_AUTOFLUSH $@ $EVAL_ERROR $! $OS_ERROR $ERRNO $? $CHILD_ERROR $^E $EXTENDED_OS_ERROR $0 $PROGRAM_NAME $$ $PROCESS_ID $PID %ENV @INC $^O $OSNAME $^X $EXECUTABLE_NAME $^W $WARNING %SIG
Perl dispose de nombreuses autres variables prédéfinies, qui sont décrites dans la page de manuel perlvar(1).
Vous devez cependant savoir que tous les identificateurs Perl qui
commencent par des chiffres ou des caractères
de contrôle ne sont pas soumis aux effets de la déclaration package
et sont toujours dans le package main
.
Les noms suivants échappent également aux effets de package
:
ARGV
, ARGVOUT
, ENV
, INC
, SIG
, STDERR
, STDIN
et
STDOUT
. Il s'agit de noms de variables ou de handle de fichiers
spéciaux, qui vous ont pour la plupart été présentés dans cet article.
Enfin, j'ai complètement ignoré $`
, $&
, $'
et autres $1
,
$+
, @+
, @-
, qui sont spécifiques aux expressions rationnelles.
Reportez vous à perlre(1) et perlretut(1) pour en connaître les arcanes.
Le manuel de Perl est une mine d'informations. Cet article reprend, détaille, agrège et ajoute des exemples de code aux informations issues des pages suivantes :
perlvar(1), Perl predefined variables.
Toutes les variables décrites ici, et bien d'autres, sont décrites en détail dans cette page de manuel.
perlop(1), Perl operators and precedence, section I/O Operators,
pour plus de détails sur le fonctionnement de l'opérateur diamant
(<>
) avec while
, @ARGV
, etc.
perlopentut(1), tutorial on opening things in Perl.
perlfunc(1), Perl builtin functions
je vous conseille plutôt de consulter perldoc -f
fonction,
qui permet d'accéder facilement à des sous-ensemble de cette page
de manuel énorme.
perlport(1), Writing portable Perl, section ISSUES, Newlines.
perlipc(1), Perl interprocess communication (signals, fifos, pipes, safe subprocesses, sockets, and semaphores)
pour ce qui concerne les signaux.
perlsub(1), Perl subroutines, sections Private Variables via my() et Temporary Values via local().
Contient également des informations sur @_
.
warnings(3pm), Perl pragma to control optional warnings et perllexwarn(1), Perl Lexical Warnings.
lib(3pm), manipulate @INC at compile time.
L'ensemble de perlfaq(1), frequently asked questions about Perl.
Toujours en anglais, mais sur un mode plus détendu :
Stages of a Perl Programmer (Novice, Initiate, User, Adpet, Hacker, Guru, Wizard), par Nathan Torkington.
Mon prochain article sera d'ailleurs consacré à la documentation de Perl, afin de vous aider à vous retrouver dans la jungle de l'information disponible.
Philippe 'BooK' Bruhat, <book@mongueurs.net>.
Philippe Bruhat est vice-président de l'association les Mongueurs de Perl, membre du groupe Paris.pm et de l'équipe organisatrice de la conférence YAPC::Europe à Paris en juillet 2003 (http://yapc.mongueurs.net/). Il est consultant en sécurité et l'auteur des modules Log::Procmail, HTTP::Proxy et Regexp::Log, disponibles sur CPAN.
BooK tient à remercier Jean Forget, qui lui a fourni un exemple tout à fait éclairant sur l'étendue de la magie à l'œuvre dans while().
Copyright © Les Mongueurs de Perl, 2001-2011
pour le site.
Les auteurs conservent le copyright de leurs articles.