Les bases de la programmation en Perl
Présentation générale du Perl
Même si le shell reste l’outil privilégié et historique pour l’écriture de procédures utilitaires sous Unix, le langage Perl s’est imposé clairement comme une alternative efficace dans cette activité et il est systématiquement intégré, sous forme de paquet logiciel, à tous les systèmes Unix ou Linux. Il nous semble donc pertinent d’enrichir notre patrimoine culturel Unix par une étude de premier niveau de ce langage, sans prétendre non plus traiter le sujet dans son intégralité. Tout comme pour le shell, nous souhaitons disposer de compétences de base qui nous permettront de lire et de maintenir des scripts existants ainsi que d’écrire des procédures simples d’exploitation.
PERL est l’acronyme de Practical Extraction and Report Language. Imaginé et créé par Larry Wall dès 1986, le langage a été développé ensuite par un grand nombre de volontaires dans une démarche structurée de logiciel libre. Il apparaît aujourd’hui comme un langage performant, stable et portable dont la pérennité est assurée.
Le Perl est particulièrement adapté à la manipulation de textes, de fichiers et de processus. Dans le cadre professionnel, Il est donc...
L’essentiel du langage
1. Structure d’un programme
Par choix de conception, il y a peu de contraintes dans ce langage. La devise officielle est TIMTOWTDI (There Is More Than One Way To Do It)
Les espaces ou tabulations ne sont nécessaires que pour lever une ambiguïté syntaxique.
Les instructions individuelles sont séparées par des ; (point-virgule).
La notion de bloc d’instructions existe, comme en langage C, via les accolades { }. Le point-virgule n’est pas obligatoire pour la dernière instruction d’un bloc.
Les commentaires débutent par un # (dièse) et se terminent à la fin de la ligne.
Seules les fonctions (sous-programmes) nécessitent une déclaration. Les autres éléments du langage sont créés automatiquement avec une valeur nulle.
Un programme Perl doit débuter par une première ligne précisant l’emplacement de l’interpréteur. La plupart du temps, on aura : #! /usr/bin/perl.
Perl analyse complètement la source avant de l’exécuter et la compile sous une forme interne non stockée sur disque. Une erreur de syntaxe sera donc toujours détectée avant le lancement du programme. De ce fait, on peut considérer que Perl est à la fois un interpréteur et un compilateur.
Il est conseillé d’utiliser systématiquement l’option -w (warning) pour obtenir des avertissements salutaires sur certaines mauvaises constructions. La première ligne d’un script Perl ressemblera donc plutôt à : #! /usr/bin/perl -w. L’interpréteur comporte bon nombre d’options supplémentaires dont certaines seront précisées au fur et à mesure de notre étude du langage.
2. Éléments du langage
Le langage propose trois types principaux de variables :
-
scalaire : un scalaire est soit un nombre, soit une chaîne de caractères soit une référence (ou pointeur ou adresse).
-
tableau : un tableau est une liste ordonnée de scalaires, indicée par des entiers.
-
tableau associatif (hachage) : un tableau associatif est un tableau plus évolué dont les indices sont des chaînes de caractères pour permettre des associations entre une clé, par définition unique...
Expressions régulières
Les expressions régulières (ou expressions rationnelles) sont très utilisées dans différents programmes du monde Unix (grep, sed, awk, vi...). Il s’agit de modèles (motifs), intégrant une sémantique, qui peuvent être comparés à des chaînes données, soit pour une simple sélection, soit pour opérer des transformations. Perl implémente un sur-ensemble des expressions régulières des outils Unix.
1. L’essentiel des motifs
Une chaîne, même littérale, est considérées comme expression régulière quand elle est encadrée par deux / (slash). Sans autre indication, elle concerne le scalaire par défaut $_.
Exemple
foreach (@ARGV)
{
# Afficher les arguments qui contiennent la chaîne "toto"
print "$_\n" if /toto/ ;
}
Toute la puissance sémantique des expressions régulières est incarnée par les différents motifs qui peuvent la constituer.
a. Motifs correspondant à un seul caractère
. (point) |
un caractère quelconque (sauf le \n) |
[ ] |
un caractère parmi ceux mentionnés entre les crochets - ((signe moins) désigne un intervalle sauf s’il est en dernier) |
[ˆ] |
un caractère n’appartenant pas à l’ensemble |
\a |
sonnerie |
\n |
saut de ligne |
\r |
retour chariot |
\t |
tabulation |
\f |
saut de page |
\e |
le caractère escape |
\007 |
un code ASCII en octal |
\x7f |
un code ASCII en hexadécimal |
\cx |
un caractère de contrôle (exemple Ctrl_x) |
\d |
un chiffre (abrégé Perl pour la notation Unix [0-9]) |
\w |
un caractère alphanumérique (abrégé pour [a-zA-Z_0-9]) |
\s |
un séparateur (espace ou \t \n \r \f) |
\D |
négation du \d |
\W |
négation du \w |
\S |
négation du \s |
b. Motifs de regroupement
Les motifs de regroupement qualifient l’élément qui les précède dans l’expression.
* |
un nombre quelconque d’exemplaires (éventuellement 0) |
+ |
un nombre quelconque d’exemplaires (au moins un) |
? |
aucun ou un seul exemplaire |
{n1,n2} |
entre n1 et n2 exemplaires |
{n1,} |
au moins n1 exemplaires |
{n1} |
exactement n1 exemplaires |
c. Parenthèses de mémorisation...
Fonctions
1. Définition et utilisation
La déclaration d’une fonction (subroutine) utilise la syntaxe suivante :
sub mafonction
{
instructions...
}
Les définitions de fonctions peuvent apparaître à tout endroit du programme. Par souci de lisibilité, on les regroupe souvent en tête de fichier. Lors de l’appel de la fonction, le nom du sous-programme peut être précédé ou non du caractère &. Le & est une survivance des très anciennes versions Perl 4, il reste obligatoire quand la fonction est appelée sans arguments et que sa définition n’a pas encore été rencontrée dans le fichier au moment de l’appel.
Les éventuels arguments sont fournis sous la forme d’une liste entourée ou non de parenthèses.
2. Paramètres, visibilité, retour
Par défaut, toute variable du script est une variable globale, connue et modifiable dans le cadre de la fonction.
L’opérateur my permet de déclarer des variables locales à la fonction. Si des variables globales de même nom existent dans le script, elles seront naturellement masquées par ces variables locales.
Les fonctions n’ont pas de prototypes et peuvent être appelées avec un nombre quelconque d’arguments.
La liste de ces arguments est disponible via un tableau @_ qui est considéré comme local pour la fonction concernée. S’il existait un tableau @_ global, celui-ci serait sauvegardé puis restauré au retour du sous-programme.
Pour une fonction appelée sans arguments mais précédée du caractère &, un éventuel tableau global @_ sera accessible dans la fonction. Ceci n’est pas le cas si l’appel est fait sans le &. Il s’agit là aussi d’une survivance des très anciennes versions Perl 4. Il faut noter également que la notion de prototype existe en fait mais elle est peu utilisée car, a priori, peu utile quand les mécanismes sont bien maîtrisés.
Toute modification du tableau d’arguments @_ entraîne la modification des variables correspondantes au niveau de l’appelant. Si cette technique est utilisée, l’appel de la fonction avec des constantes...
Gestion des fichiers et des répertoires
1. Ouverture de fichier et entrées/sorties
Les fichiers vont être manipulés via des descripteurs obtenus auprès d’une fonction d’ouverture. Trois descripteurs sont immédiatement disponibles sans ouverture explicite nécessaire :
STDIN |
entrée standard |
STDOUT |
sortie standard |
STDERR |
erreur standard |
La fonction open permet d’obtenir un descripteur de fichier en précisant le mode d’utilisation souhaité. Un descripteur est un identificateur particulier (ce n’est pas un scalaire). Il est conseillé d’utiliser les majuscules pour les noms de descripteurs afin d’éviter des conflits accidentels avec les mots réservés du langage (présents ou futurs).
open (DESC , "fichier") ;
Par défaut, l’ouverture se fait en lecture seule sur un fichier existant.
La syntaxe open (DESC , "fichier") ; équivaut à open (DESC , "< fichier") ;.
Les autres modes sont :
"> fichier" |
écriture (création ou écrasement). |
">> fichier" |
écriture en fin de fichier (création éventuelle). |
"+< fichier" |
lecture/écriture sur fichier existant (mode mise à jour). |
"+> fichier" |
identique au mode ”>” avec la lecture en plus. |
"+>> fichier" |
identique au mode ”>>” avec la lecture en plus. |
La fonction open retourne vrai ou faux et doit bien entendu être testée pour s’assurer que le descripteur est valide. La fonction die lui est souvent associée dans ce contexte. Cette fonction affiche un message sur l’erreur standard (la variable $! permet alors d’y intégrer le libellé de l’erreur système) puis elle met fin au processus avec un code non nul. La fonction warn est également disponible. Elle affiche aussi un message sur l’erreur standard mais sans mettre fin au processus.
Exemple
print("Nom de fichier : ") ;
chomp($var = <STDIN>) ;
open(DESC,$var) || die "Erreur d'ouverture pour $var : $!\n" ;
Dans cet exemple, puisque nous utilisons l’opérateur || (ou), la fonction die ne sera appelée que si la fonction open échoue.
La fonction close permet de mettre fin à l’utilisation...
Quelques fonctionnalités complémentaires
1. Accès aux informations du système
Dans un contexte liste, les fonctions getpwuid et getpwnam retournent le contenu d’une ligne du fichier /etc/passwd. Elles reçoivent respectivement en argument un numéro (UID) ou un nom d’utilisateur :
($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell)
= getpwuid (1001);
ou bien :
($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell)
= getpwnam ("toto");
Le champ quota est toujours vide. Les champs comment et gcos contiennent la même valeur.
Pour obtenir uniquement certains champs, on utilisera une tranche de liste :
($nom,$repertoire) = (getpwnam("toto"))[0,7] ;
Dans un contexte scalaire, ces fonctions retournent la correspondance demandée :
$uid = getpwnam("toto") ;
$nom = getpwuid(1001) ;
Les fonctions setpwent, getpwent, endpwent permettent un parcours séquentiel du fichier des comptes /etc/passwd.
setpwent() ;
while ( @list = getpwent() )
# Chaque appel retourne une liste identique à celle de "getpwnam"
{ .............. }
endpwent() ;
Les fonctions getgrgid et getgrnam retournent le contenu d’une ligne du fichier /etc/group sous la forme d’une liste. Elles reçoivent respectivement en argument un numéro (GID) ou un nom de groupe.
Les fonctions setgrent, getgrent, endgrent permettent un parcours séquentiel du fichier des groupes /etc/group.
2. Gestion des processus
a. La fonction system
Cette fonction permet d’exécuter la commande passée en argument. Elle retourne sa valeur de sortie (code exit). Un processus shell intermédiaire est utilisé pour l’exécution du programme.
Exemple
system("date") ;
La commande lancée hérite naturellement des descripteurs de fichiers standard (entrée, sortie et erreur) mais des redirections peuvent être effectuées au moment de l’appel.
La commande hérite également de l’environnement via le hachage prédéfini %ENV.
b. Les quotes inverses
Tout comme en shell, les ` ` (quotes inverses) déclenchent l’exécution d’une commande et en restituent la sortie standard.
Exemple
$chaine = "La date est " . `date` ;
La fonction readpipe effectue le même traitement...